blob: 4b7ab6935e720b9b49f174e2701bc8fbadcfb0ec [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
17#include <stdint.h>
18#include <stdlib.h>
19#include <errno.h>
20#include <string.h>
21#include <poll.h>
22#include <sys/types.h>
23#include <sys/socket.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020024#include <sys/un.h>
25#include <netinet/in.h>
26#include <netinet/tcp.h>
Michal Vasko086311b2016-01-08 09:53:11 +010027#include <arpa/inet.h>
28#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010029#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010030#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010031#include <time.h>
Michal Vaskoade892d2017-02-22 13:40:35 +010032#include <signal.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020033#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010034
Michal Vasko1a38c862016-01-15 15:50:07 +010035#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010036#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020037#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010038
Michal Vaskob48aa812016-01-18 14:13:09 +010039struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010040#ifdef NC_ENABLED_SSH
41 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
42#endif
43 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020044 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
45 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010046};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010047
fanchanghu966f2de2016-07-21 02:28:57 -040048static nc_rpc_clb global_rpc_clb = NULL;
49
Michal Vasko3031aae2016-01-27 16:07:18 +010050struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010051nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010052{
53 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010054 struct nc_endpt *endpt = NULL;
55
Michal Vaskoddce1212019-05-24 09:58:49 +020056 if (!name) {
57 ERRARG("endpt_name");
58 return NULL;
59 }
60
Michal Vaskoade892d2017-02-22 13:40:35 +010061 /* WRITE LOCK */
62 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010063
64 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020065 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010066 endpt = &server_opts.endpts[i];
67 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010068 }
69 }
70
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010071 if (!endpt) {
72 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010073 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020074 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010075 return NULL;
76 }
77
Michal Vaskoe2713da2016-08-22 16:06:40 +020078 if (idx) {
79 *idx = i;
80 }
81
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010082 return endpt;
83}
84
Michal Vaskoadf30f02019-06-24 09:34:47 +020085struct nc_ch_endpt *
86nc_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 +010087{
Michal Vaskoadf30f02019-06-24 09:34:47 +020088 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020089 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020090 struct nc_ch_endpt *endpt = NULL;
91
92 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +020093
Michal Vaskoddce1212019-05-24 09:58:49 +020094 if (!name) {
95 ERRARG("client_name");
96 return NULL;
97 }
98
Michal Vasko2e6defd2016-10-07 15:48:15 +020099 /* READ LOCK */
100 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
101
102 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200103 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200104 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200105 if (!endpt_name && !ti) {
106 /* return only client */
107 break;
108 }
109 for (j = 0; j < client->ch_endpt_count; ++j) {
110 if (!strcmp(client->ch_endpts[j].name, endpt_name) && (!ti || (ti == client->ch_endpts[j].ti))) {
111 endpt = &client->ch_endpts[j];
112 break;
113 }
114 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200115 break;
116 }
117 }
118
119 if (!client) {
120 ERR("Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200121
Michal Vasko2e6defd2016-10-07 15:48:15 +0200122 /* READ UNLOCK */
123 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200124 } else if (endpt_name && ti && !endpt) {
125 ERR("Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
126
127 /* READ UNLOCK */
128 pthread_rwlock_unlock(&server_opts.ch_client_lock);
129 } else {
130 /* CH CLIENT LOCK */
131 pthread_mutex_lock(&client->lock);
132
133 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200134 }
135
Michal Vaskoadf30f02019-06-24 09:34:47 +0200136 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200137}
138
139void
140nc_server_ch_client_unlock(struct nc_ch_client *client)
141{
142 /* CH CLIENT UNLOCK */
143 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100144
145 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200146 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100147}
Michal Vasko086311b2016-01-08 09:53:11 +0100148
Michal Vasko1a38c862016-01-15 15:50:07 +0100149API void
150nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
151{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200152 if (!session) {
153 ERRARG("session");
154 return;
155 } else if (!reason) {
156 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100157 return;
158 }
159
Michal Vasko142cfea2017-08-07 10:12:11 +0200160 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
161 session->killed_by = 0;
162 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100163 session->term_reason = reason;
164}
165
Michal Vasko142cfea2017-08-07 10:12:11 +0200166API void
167nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
168{
169 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
170 ERRARG("session");
171 return;
172 } else if (!sid) {
173 ERRARG("sid");
174 return;
175 }
176
177 session->killed_by = sid;
178}
179
180API void
181nc_session_set_status(struct nc_session *session, NC_STATUS status)
182{
183 if (!session) {
184 ERRARG("session");
185 return;
186 } else if (!status) {
187 ERRARG("status");
188 return;
189 }
190
191 session->status = status;
192}
193
Michal Vasko086311b2016-01-08 09:53:11 +0100194int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200195nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100196{
Michal Vasko06c860d2018-07-09 16:08:52 +0200197 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100198 int is_ipv4, sock;
199 struct sockaddr_storage saddr;
200
201 struct sockaddr_in *saddr4;
202 struct sockaddr_in6 *saddr6;
203
204
205 if (!strchr(address, ':')) {
206 is_ipv4 = 1;
207 } else {
208 is_ipv4 = 0;
209 }
210
211 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
212 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100213 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100214 goto fail;
215 }
216
Michal Vaskobe52dc22018-10-17 09:28:17 +0200217 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200218 opt = 1;
219 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
220 ERR("Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
221 goto fail;
222 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100223 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
224 ERR("Could not set TCP_NODELAY socket option (%s).", strerror(errno));
225 goto fail;
226 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200227
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200228 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100229 goto fail;
230 }
231
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200232 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100233 if (is_ipv4) {
234 saddr4 = (struct sockaddr_in *)&saddr;
235
236 saddr4->sin_family = AF_INET;
237 saddr4->sin_port = htons(port);
238
239 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100240 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100241 goto fail;
242 }
243
244 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100245 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100246 goto fail;
247 }
248
249 } else {
250 saddr6 = (struct sockaddr_in6 *)&saddr;
251
252 saddr6->sin6_family = AF_INET6;
253 saddr6->sin6_port = htons(port);
254
255 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100256 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100257 goto fail;
258 }
259
260 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100261 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100262 goto fail;
263 }
264 }
265
Michal Vaskofb89d772016-01-08 12:25:35 +0100266 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100267 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100268 goto fail;
269 }
270
271 return sock;
272
273fail:
274 if (sock > -1) {
275 close(sock);
276 }
277
278 return -1;
279}
280
281int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200282nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
283{
284 struct sockaddr_un sun;
285 int sock = -1;
286
287 sock = socket(AF_UNIX, SOCK_STREAM, 0);
288 if (sock == -1) {
289 ERR("Failed to create socket (%s).", strerror(errno));
290 goto fail;
291 }
292
293 memset(&sun, 0, sizeof(sun));
294 sun.sun_family = AF_UNIX;
295 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
296
297 unlink(sun.sun_path);
298 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
299 ERR("Could not bind \"%s\" (%s).", address, strerror(errno));
300 goto fail;
301 }
302
303 if (opts->mode != (mode_t)-1) {
304 if (chmod(sun.sun_path, opts->mode) < 0) {
305 ERR("Failed to set unix socket permissions (%s).", strerror(errno));
306 goto fail;
307 }
308 }
309
310 if (opts->uid != (uid_t)-1 || opts->gid != (gid_t)-1) {
311 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
312 ERR("Failed to set unix socket uid/gid (%s).", strerror(errno));
313 goto fail;
314 }
315 }
316
317 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
318 ERR("Unable to start listening on \"%s\" (%s).", address, strerror(errno));
319 goto fail;
320 }
321
322 return sock;
323
324fail:
325 if (sock > -1) {
326 close(sock);
327 }
328
329 return -1;
330}
331
aPiecek90ff0242021-02-14 14:58:01 +0100332/**
333 * @brief Evaluate socket name for AF_UNIX socket.
334 * @param[in] acc_sock_fd is file descriptor for the accepted socket (a nonnegative).
335 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
336 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
337 * @return 0 if the stream socket is unnamed. Parameter host is set to NULL.
338 * @return -1 in case of error. Parameter host is set to NULL.
339 */
340static int
341sock_host_unix(int acc_sock_fd, char **host)
342{
343 char *sun_path;
344 struct sockaddr_storage saddr;
345 socklen_t addr_len;
346
347 *host = NULL;
348 saddr.ss_family = AF_UNIX;
349 addr_len = sizeof(saddr);
350
351 if (getsockname(acc_sock_fd, (struct sockaddr *)&saddr, &addr_len)) {
352 ERR("getsockname failed (%s).", strerror(errno));
353 return -1;
354 }
355
356 sun_path = ((struct sockaddr_un *)&saddr)->sun_path;
357 if (!sun_path) {
358 /* stream socket is unnamed */
359 return 0;
360 }
361
362 if (!(*host = strdup(sun_path))) {
363 ERRMEM;
364 return -1;
365 }
366
367 return 0;
368}
369
370/**
371 * @brief Evaluate socket name and port number for AF_INET socket.
372 * @param[in] addr is pointing to structure filled by accept function which was successful.
373 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
374 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
375 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
376 * @return -1 in case of error. Parameter host is set to NULL and port is unchanged.
377 */
378static int
379sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port)
380{
381 *host = malloc(INET_ADDRSTRLEN);
382 if (!(*host)) {
383 ERRMEM;
384 return -1;
385 }
386
aPiecek3da9b342021-02-18 15:00:03 +0100387 if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) {
aPiecek90ff0242021-02-14 14:58:01 +0100388 ERR("inet_ntop failed(%s).");
389 free(*host);
390 *host = NULL;
391 return -1;
392 }
393
394 if (port) {
395 *port = ntohs(addr->sin_port);
396 }
397
398 return 0;
399}
400
401/**
402 * @brief Evaluate socket name and port number for AF_INET6 socket.
403 * @param[in] addr is pointing to structure filled by accept function which was successful.
404 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
405 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
406 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
407 * @return -1 in case of error. Parameter host is set to the NULL and port is unchanged.
408 */
409static int
410sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port)
411{
412 *host = malloc(INET6_ADDRSTRLEN);
413 if (!(*host)) {
414 ERRMEM;
415 return -1;
416 }
417
aPiecek3da9b342021-02-18 15:00:03 +0100418 if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) {
aPiecek90ff0242021-02-14 14:58:01 +0100419 ERR("inet_ntop failed(%s).");
420 free(*host);
421 *host = NULL;
422 return -1;
423 }
424
425 if (port) {
426 *port = ntohs(addr->sin6_port);
427 }
428
429 return 0;
430}
431
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200432int
Michal Vasko3031aae2016-01-27 16:07:18 +0100433nc_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 +0100434{
Michal Vaskof54cd352017-02-22 13:42:02 +0100435 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100436 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100437 struct pollfd *pfd;
438 struct sockaddr_storage saddr;
439 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100440 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100441
442 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100443 if (!pfd) {
444 ERRMEM;
445 return -1;
446 }
447
Michal Vaskoac2f6182017-01-30 14:32:03 +0100448 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200449 if (binds[i].sock < 0) {
450 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200451 continue;
452 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200453 if (binds[i].pollin) {
454 binds[i].pollin = 0;
455 /* leftover pollin */
456 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100457 break;
458 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100459 pfd[pfd_count].fd = binds[i].sock;
460 pfd[pfd_count].events = POLLIN;
461 pfd[pfd_count].revents = 0;
462
463 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100464 }
465
Michal Vasko0a3f3752016-10-13 14:58:38 +0200466 if (sock == -1) {
467 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100468 sigfillset(&sigmask);
469 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100470 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100471 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
472
Michal Vasko0a3f3752016-10-13 14:58:38 +0200473 if (!ret) {
474 /* we timeouted */
475 free(pfd);
476 return 0;
477 } else if (ret == -1) {
478 ERR("Poll failed (%s).", strerror(errno));
479 free(pfd);
480 return -1;
481 }
Michal Vasko086311b2016-01-08 09:53:11 +0100482
Michal Vaskoac2f6182017-01-30 14:32:03 +0100483 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
484 /* adjust i so that indices in binds and pfd always match */
485 while (binds[i].sock != pfd[j].fd) {
486 ++i;
487 }
488
489 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200490 --ret;
491
492 if (!ret) {
493 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100494 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200495 break;
496 } else {
497 /* just remember the event for next time */
498 binds[i].pollin = 1;
499 }
500 }
Michal Vasko086311b2016-01-08 09:53:11 +0100501 }
502 }
503 free(pfd);
504
505 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100506 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100507 return -1;
508 }
509
510 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100511 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100512 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100513 return -1;
514 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200515 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100516
Michal Vasko0190bc32016-03-02 15:47:49 +0100517 /* make the socket non-blocking */
518 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
519 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100520 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100521 return -1;
522 }
523
Michal Vasko3031aae2016-01-27 16:07:18 +0100524 if (idx) {
525 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100526 }
527
aPiecek90ff0242021-02-14 14:58:01 +0100528 if (!host) {
529 return ret;
530 }
Michal Vasko086311b2016-01-08 09:53:11 +0100531
aPiecek90ff0242021-02-14 14:58:01 +0100532 if (port) {
533 *port = 0;
534 }
Michal Vasko086311b2016-01-08 09:53:11 +0100535
aPiecek90ff0242021-02-14 14:58:01 +0100536 if (saddr.ss_family == AF_UNIX) {
537 sock_host_unix(ret, host);
538 } else if (saddr.ss_family == AF_INET) {
539 sock_host_inet((struct sockaddr_in *)&saddr, host, port);
540 } else if (saddr.ss_family == AF_INET6) {
541 sock_host_inet6((struct sockaddr_in6 *)&saddr, host, port);
542 } else {
543 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100544 }
545
546 return ret;
547}
548
Michal Vasko05ba9df2016-01-13 14:40:27 +0100549static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100550nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100551{
Michal Vasko77367452021-02-16 16:32:18 +0100552 const char *identifier = NULL, *revision = NULL, *format = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100553 char *model_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100554 struct ly_out *out;
Michal Vasko9b1a9522021-03-15 16:24:26 +0100555 const struct lys_module *module = NULL, *mod;
Michal Vasko77367452021-02-16 16:32:18 +0100556 const struct lysp_submodule *submodule = NULL;
557 struct lyd_node *child, *err, *data = NULL;
558 LYS_OUTFORMAT outformat = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100559
Michal Vasko77367452021-02-16 16:32:18 +0100560 LY_LIST_FOR(lyd_child(rpc), child) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100561 if (!strcmp(child->schema->name, "identifier")) {
Michal Vasko77367452021-02-16 16:32:18 +0100562 identifier = LYD_CANON_VALUE(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100563 } else if (!strcmp(child->schema->name, "version")) {
Michal Vasko77367452021-02-16 16:32:18 +0100564 revision = LYD_CANON_VALUE(child);
565 if (revision && revision[0] == '\0') {
566 revision = NULL;
Radek Krejci1afa7792017-03-26 11:24:16 -0500567 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100568 } else if (!strcmp(child->schema->name, "format")) {
Michal Vasko77367452021-02-16 16:32:18 +0100569 format = LYD_CANON_VALUE(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100570 }
571 }
Michal Vasko77367452021-02-16 16:32:18 +0100572 VRB("Schema \"%s@%s\" was requested.", identifier, revision ? revision : "<any>");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100573
Michal Vasko77367452021-02-16 16:32:18 +0100574 /* check revision */
575 if (revision && (strlen(revision) != 10) && strcmp(revision, "1.0")) {
576 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100577 nc_err_set_msg(err, "The requested version is not supported.", "en");
578 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100579 }
580
Michal Vasko77367452021-02-16 16:32:18 +0100581 if (revision) {
582 /* get specific module */
583 module = ly_ctx_get_module(server_opts.ctx, identifier, revision);
584 if (!module) {
585 submodule = ly_ctx_get_submodule(server_opts.ctx, identifier, revision);
586 }
587 } else {
588 /* try to get implemented, then latest module */
589 module = ly_ctx_get_module_implemented(server_opts.ctx, identifier);
590 if (!module) {
591 module = ly_ctx_get_module_latest(server_opts.ctx, identifier);
592 }
593 if (!module) {
594 submodule = ly_ctx_get_submodule_latest(server_opts.ctx, identifier);
595 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200596 }
Michal Vasko77367452021-02-16 16:32:18 +0100597 if (!module && !submodule) {
598 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100599 nc_err_set_msg(err, "The requested schema was not found.", "en");
600 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100601 }
602
603 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100604 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko77367452021-02-16 16:32:18 +0100605 outformat = LYS_OUT_YANG;
Radek Krejci90fba642016-12-07 15:59:45 +0100606 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko77367452021-02-16 16:32:18 +0100607 outformat = LYS_OUT_YIN;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100608 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100609 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100610 nc_err_set_msg(err, "The requested format is not supported.", "en");
611 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100612 }
Michal Vasko77367452021-02-16 16:32:18 +0100613
614 /* print */
615 ly_out_new_memory(&model_data, 0, &out);
616 if (module) {
617 lys_print_module(out, module, outformat, 0, 0);
618 } else {
619 lys_print_submodule(out, submodule, outformat, 0, 0);
620 }
621 ly_out_free(out, NULL, 0);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200622 if (!model_data) {
623 ERRINT;
624 return NULL;
625 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100626
Michal Vasko9b1a9522021-03-15 16:24:26 +0100627 /* create reply */
628 mod = ly_ctx_get_module_implemented(server_opts.ctx, "ietf-netconf-monitoring");
629 if (!mod || lyd_new_inner(NULL, mod, "get-schema", 0, &data)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100630 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200631 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100632 return NULL;
633 }
Michal Vasko9b1a9522021-03-15 16:24:26 +0100634 lydict_insert_zc(server_opts.ctx, model_data, (const char **)&model_data);
635 if (lyd_new_any(data, NULL, "data", model_data, 1, LYD_ANYDATA_STRING, 1, NULL)) {
636 ERRINT;
637 lydict_remove(server_opts.ctx, model_data);
638 lyd_free_tree(data);
639 return NULL;
640 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100641
Radek Krejci36dfdb32016-09-01 16:56:35 +0200642 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100643}
644
645static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100646nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100647{
Michal Vasko428087d2016-01-14 16:04:28 +0100648 session->term_reason = NC_SESSION_TERM_CLOSED;
649 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100650}
651
Michal Vasko086311b2016-01-08 09:53:11 +0100652API int
653nc_server_init(struct ly_ctx *ctx)
654{
Michal Vasko77367452021-02-16 16:32:18 +0100655 struct lysc_node *rpc;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000656 pthread_rwlockattr_t attr;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100657
Michal Vasko086311b2016-01-08 09:53:11 +0100658 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200659 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100660 return -1;
661 }
662
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100663 nc_init();
664
Michal Vasko05ba9df2016-01-13 14:40:27 +0100665 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100666 rpc = NULL;
667 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
668 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
669 }
Michal Vasko88639e92017-08-03 14:38:10 +0200670 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100671 rpc->priv = nc_clb_default_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100672 }
673
674 /* set default <close-session> callback if not specififed */
Michal Vasko77367452021-02-16 16:32:18 +0100675 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200676 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100677 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100678 }
679
Michal Vasko086311b2016-01-08 09:53:11 +0100680 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100681
682 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500683 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100684
Michal Vasko77367452021-02-16 16:32:18 +0100685 errno = 0;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000686
687 if (pthread_rwlockattr_init(&attr) == 0) {
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700688#if defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
Frank Rimpler9f838b02018-07-25 06:44:03 +0000689 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
690 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
691 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
692 }
693 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
694 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
695 }
696 } else {
697 ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
698 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700699#endif
Frank Rimpler9f838b02018-07-25 06:44:03 +0000700 pthread_rwlockattr_destroy(&attr);
701 } else {
702 ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
703 }
Michal Vasko086311b2016-01-08 09:53:11 +0100704 return 0;
705}
706
Michal Vaskob48aa812016-01-18 14:13:09 +0100707API void
708nc_server_destroy(void)
709{
Michal Vasko1440a742021-03-31 11:11:03 +0200710 uint32_t i;
Radek Krejci658782b2016-12-04 22:04:55 +0100711
712 for (i = 0; i < server_opts.capabilities_count; i++) {
713 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
714 }
715 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200716 server_opts.capabilities = NULL;
717 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200718 if (server_opts.content_id_data && server_opts.content_id_data_free) {
719 server_opts.content_id_data_free(server_opts.content_id_data);
720 }
721 server_opts.passwd_auth_data = NULL;
722 server_opts.passwd_auth_data_free = NULL;
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200723
Radek Krejci53691be2016-02-22 13:58:37 +0100724#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100725 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200726 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100727#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100728#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100729 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
730 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
731 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200732 server_opts.passwd_auth_data = NULL;
733 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100734
Michal Vasko17dfda92016-12-01 14:06:16 +0100735 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100736
737 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
738 server_opts.hostkey_data_free(server_opts.hostkey_data);
739 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200740 server_opts.hostkey_data = NULL;
741 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100742#endif
743#ifdef NC_ENABLED_TLS
744 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
745 server_opts.server_cert_data_free(server_opts.server_cert_data);
746 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200747 server_opts.server_cert_data = NULL;
748 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100749 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
750 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
751 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200752 server_opts.trusted_cert_list_data = NULL;
753 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100754#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100755 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100756}
757
Michal Vasko086311b2016-01-08 09:53:11 +0100758API int
759nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
760{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200761 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
762 ERRARG("basic_mode");
763 return -1;
764 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
765 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100766 return -1;
767 }
768
769 server_opts.wd_basic_mode = basic_mode;
770 server_opts.wd_also_supported = also_supported;
771 return 0;
772}
773
Michal Vasko1a38c862016-01-15 15:50:07 +0100774API void
Michal Vasko55f03972016-04-13 08:56:01 +0200775nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
776{
777 if (!basic_mode && !also_supported) {
778 ERRARG("basic_mode and also_supported");
779 return;
780 }
781
782 if (basic_mode) {
783 *basic_mode = server_opts.wd_basic_mode;
784 }
785 if (also_supported) {
786 *also_supported = server_opts.wd_also_supported;
787 }
788}
789
Michal Vasko55f03972016-04-13 08:56:01 +0200790API int
Radek Krejci658782b2016-12-04 22:04:55 +0100791nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200792{
Radek Krejci658782b2016-12-04 22:04:55 +0100793 const char **new;
794
795 if (!value || !value[0]) {
796 ERRARG("value must not be empty");
797 return EXIT_FAILURE;
798 }
799
800 server_opts.capabilities_count++;
801 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
802 if (!new) {
803 ERRMEM;
804 return EXIT_FAILURE;
805 }
806 server_opts.capabilities = new;
Michal Vasko77367452021-02-16 16:32:18 +0100807 lydict_insert(server_opts.ctx, value, 0, &server_opts.capabilities[server_opts.capabilities_count - 1]);
Radek Krejci658782b2016-12-04 22:04:55 +0100808
809 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200810}
811
Michal Vasko1a38c862016-01-15 15:50:07 +0100812API void
Michal Vasko1440a742021-03-31 11:11:03 +0200813nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
814 void (*free_user_data)(void *user_data))
815{
816 server_opts.content_id_clb = content_id_clb;
817 server_opts.content_id_data = user_data;
818 server_opts.content_id_data_free = free_user_data;
819}
820
821API void
Michal Vasko086311b2016-01-08 09:53:11 +0100822nc_server_set_hello_timeout(uint16_t hello_timeout)
823{
Michal Vasko086311b2016-01-08 09:53:11 +0100824 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100825}
826
Michal Vasko55f03972016-04-13 08:56:01 +0200827API uint16_t
828nc_server_get_hello_timeout(void)
829{
830 return server_opts.hello_timeout;
831}
832
Michal Vasko1a38c862016-01-15 15:50:07 +0100833API void
Michal Vasko086311b2016-01-08 09:53:11 +0100834nc_server_set_idle_timeout(uint16_t idle_timeout)
835{
Michal Vasko086311b2016-01-08 09:53:11 +0100836 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100837}
838
Michal Vasko55f03972016-04-13 08:56:01 +0200839API uint16_t
840nc_server_get_idle_timeout(void)
841{
842 return server_opts.idle_timeout;
843}
844
Michal Vasko71090fc2016-05-24 16:37:28 +0200845API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100846nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100847{
Michal Vasko71090fc2016-05-24 16:37:28 +0200848 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200849 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200850
Michal Vasko45e53ae2016-04-07 11:46:03 +0200851 if (!server_opts.ctx) {
852 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200853 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200854 } else if (fdin < 0) {
855 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200856 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200857 } else if (fdout < 0) {
858 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200859 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200860 } else if (!username) {
861 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200862 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200863 } else if (!session) {
864 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200865 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100866 }
867
868 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200869 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100870 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100871 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200872 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100873 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100874 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100875
Michal Vasko086311b2016-01-08 09:53:11 +0100876 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100877 (*session)->ti_type = NC_TI_FD;
878 (*session)->ti.fd.in = fdin;
879 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100880
881 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100882 (*session)->flags = NC_SESSION_SHAREDCTX;
883 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100884
Michal Vaskob48aa812016-01-18 14:13:09 +0100885 /* assign new SID atomically */
Michal Vasko7f1fa3c2020-09-08 16:30:41 +0200886 (*session)->id = ATOMIC_INC(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100887
Michal Vasko086311b2016-01-08 09:53:11 +0100888 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200889 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200890 if (msgtype != NC_MSG_HELLO) {
891 nc_session_free(*session, NULL);
892 *session = NULL;
893 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100894 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200895
896 nc_gettimespec_mono(&ts_cur);
897 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
898 nc_gettimespec_real(&ts_cur);
899 (*session)->opts.server.session_start = ts_cur.tv_sec;
900
Michal Vasko1a38c862016-01-15 15:50:07 +0100901 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100902
Michal Vasko71090fc2016-05-24 16:37:28 +0200903 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100904}
Michal Vasko9e036d52016-01-08 10:49:26 +0100905
Michal Vaskob30b99c2016-07-26 11:35:43 +0200906static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100907nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
908{
909 uint8_t q_last;
910
911 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
912 ERRINT;
913 return;
914 }
915
916 /* get a unique queue value (by adding 1 to the last added value, if any) */
917 if (ps->queue_len) {
918 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
919 *id = ps->queue[q_last] + 1;
920 } else {
921 *id = 0;
922 }
923
924 /* add the id into the queue */
925 ++ps->queue_len;
926 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
927 ps->queue[q_last] = *id;
928}
929
930static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200931nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
932{
Michal Vasko74c345f2018-02-07 10:37:11 +0100933 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200934
935 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100936 /* get the actual queue idx */
937 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200938
939 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100940 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200941 /* another equal value, simply cannot be */
942 ERRINT;
943 }
Michal Vaskod8340032018-02-12 14:41:00 +0100944 if (found == 2) {
945 /* move the following values */
946 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
947 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100948 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200949 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100950 if (i == 0) {
951 found = 1;
952 } else {
953 /* this is not okay, our id is in the middle of the queue */
954 found = 2;
955 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200956 }
957 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200958 if (!found) {
959 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100960 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200961 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100962
Michal Vasko103fe632018-02-12 16:37:45 +0100963 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100964 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100965 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100966 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
967 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200968}
969
Michal Vaskof04a52a2016-04-07 10:52:10 +0200970int
Michal Vasko26043172016-07-26 14:08:59 +0200971nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200972{
973 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200974 struct timespec ts;
975
Michal Vasko77a6abe2017-10-05 10:02:20 +0200976 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100977 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200978
979 /* LOCK */
980 ret = pthread_mutex_timedlock(&ps->lock, &ts);
981 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200982 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200983 return -1;
984 }
985
Michal Vasko74c345f2018-02-07 10:37:11 +0100986 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100987 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100988 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100989 pthread_mutex_unlock(&ps->lock);
990 return -1;
991 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100992
993 /* add ourselves into the queue */
994 nc_ps_queue_add_id(ps, id);
Michal Vasko2733aad2020-04-16 09:01:52 +0200995 DBL("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 +0200996 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200997
998 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200999 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001000 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +01001001 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001002
1003 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
1004 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301005 /**
1006 * This may happen when another thread releases the lock and broadcasts the condition
1007 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1008 * but when actually this thread was ready for condition.
1009 */
preetbhansali629dfc42018-12-17 16:04:40 +05301010 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301011 break;
1012 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001013
Michal Vasko26043172016-07-26 14:08:59 +02001014 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001015 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001016 nc_ps_queue_remove_id(ps, *id);
1017 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001018 return -1;
1019 }
1020 }
1021
Michal Vaskobe86fe32016-04-07 10:43:03 +02001022 /* UNLOCK */
1023 pthread_mutex_unlock(&ps->lock);
1024
1025 return 0;
1026}
1027
Michal Vaskof04a52a2016-04-07 10:52:10 +02001028int
Michal Vasko26043172016-07-26 14:08:59 +02001029nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001030{
1031 int ret;
1032 struct timespec ts;
1033
Michal Vasko77a6abe2017-10-05 10:02:20 +02001034 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +01001035 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001036
1037 /* LOCK */
1038 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1039 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +02001040 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001041 ret = -1;
1042 }
1043
Michal Vaskob30b99c2016-07-26 11:35:43 +02001044 /* we must be the first, it was our turn after all, right? */
1045 if (ps->queue[ps->queue_begin] != id) {
1046 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001047 /* UNLOCK */
1048 if (!ret) {
1049 pthread_mutex_unlock(&ps->lock);
1050 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001051 return -1;
1052 }
1053
Michal Vaskobe86fe32016-04-07 10:43:03 +02001054 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001055 nc_ps_queue_remove_id(ps, id);
Michal Vasko2733aad2020-04-16 09:01:52 +02001056 DBL("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 +02001057 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001058
1059 /* broadcast to all other threads that the queue moved */
1060 pthread_cond_broadcast(&ps->cond);
1061
Michal Vaskobe86fe32016-04-07 10:43:03 +02001062 /* UNLOCK */
1063 if (!ret) {
1064 pthread_mutex_unlock(&ps->lock);
1065 }
1066
1067 return ret;
1068}
1069
Michal Vasko428087d2016-01-14 16:04:28 +01001070API struct nc_pollsession *
1071nc_ps_new(void)
1072{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001073 struct nc_pollsession *ps;
1074
1075 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001076 if (!ps) {
1077 ERRMEM;
1078 return NULL;
1079 }
Michal Vaskobe86fe32016-04-07 10:43:03 +02001080 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001081 pthread_mutex_init(&ps->lock, NULL);
1082
1083 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001084}
1085
1086API void
1087nc_ps_free(struct nc_pollsession *ps)
1088{
fanchanghu3d4e7212017-08-09 09:42:30 +08001089 uint16_t i;
1090
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001091 if (!ps) {
1092 return;
1093 }
1094
Michal Vaskobe86fe32016-04-07 10:43:03 +02001095 if (ps->queue_len) {
1096 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
1097 }
1098
fanchanghu3d4e7212017-08-09 09:42:30 +08001099 for (i = 0; i < ps->session_count; i++) {
1100 free(ps->sessions[i]);
1101 }
1102
Michal Vasko428087d2016-01-14 16:04:28 +01001103 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001104 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001105 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001106
Michal Vasko428087d2016-01-14 16:04:28 +01001107 free(ps);
1108}
1109
1110API int
1111nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1112{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001113 uint8_t q_id;
1114
Michal Vasko45e53ae2016-04-07 11:46:03 +02001115 if (!ps) {
1116 ERRARG("ps");
1117 return -1;
1118 } else if (!session) {
1119 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001120 return -1;
1121 }
1122
Michal Vasko48a63ed2016-03-01 09:48:21 +01001123 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001124 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001125 return -1;
1126 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001127
Michal Vasko428087d2016-01-14 16:04:28 +01001128 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001129 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001130 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001131 ERRMEM;
1132 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001133 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001134 return -1;
1135 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001136 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1137 if (!ps->sessions[ps->session_count - 1]) {
1138 ERRMEM;
1139 --ps->session_count;
1140 /* UNLOCK */
1141 nc_ps_unlock(ps, q_id, __func__);
1142 return -1;
1143 }
1144 ps->sessions[ps->session_count - 1]->session = session;
1145 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001146
Michal Vasko48a63ed2016-03-01 09:48:21 +01001147 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001148 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001149}
1150
Michal Vasko48a63ed2016-03-01 09:48:21 +01001151static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001152_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001153{
1154 uint16_t i;
1155
Radek Krejcid5f978f2016-03-03 13:14:45 +01001156 if (index >= 0) {
1157 i = (uint16_t)index;
1158 goto remove;
1159 }
Michal Vasko428087d2016-01-14 16:04:28 +01001160 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001161 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001162remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001163 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001164 if (i <= ps->session_count) {
1165 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001166 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001167 }
1168 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001169 free(ps->sessions);
1170 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001171 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001172 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001173 return 0;
1174 }
1175 }
1176
Michal Vaskof0537d82016-01-29 14:42:38 +01001177 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001178}
1179
Michal Vasko48a63ed2016-03-01 09:48:21 +01001180API int
1181nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1182{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001183 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001184 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001185
Michal Vasko45e53ae2016-04-07 11:46:03 +02001186 if (!ps) {
1187 ERRARG("ps");
1188 return -1;
1189 } else if (!session) {
1190 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001191 return -1;
1192 }
1193
1194 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001195 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001196 return -1;
1197 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001198
Radek Krejcid5f978f2016-03-03 13:14:45 +01001199 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001200
1201 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001202 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001203
Michal Vaskobe86fe32016-04-07 10:43:03 +02001204 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001205}
1206
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001207API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001208nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001209{
1210 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001211 struct nc_session *ret = NULL;
1212
1213 if (!ps) {
1214 ERRARG("ps");
1215 return NULL;
1216 }
1217
1218 /* LOCK */
1219 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1220 return NULL;
1221 }
1222
Michal Vasko4871c9d2017-10-09 14:48:39 +02001223 if (idx < ps->session_count) {
1224 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001225 }
1226
1227 /* UNLOCK */
1228 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1229
1230 return ret;
1231}
1232
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001233API uint16_t
1234nc_ps_session_count(struct nc_pollsession *ps)
1235{
Michal Vasko47003942019-03-14 12:25:23 +01001236 uint8_t q_id;
1237 uint16_t session_count;
1238
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001239 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001240 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001241 return 0;
1242 }
1243
Michal Vasko47003942019-03-14 12:25:23 +01001244 /* LOCK (just for memory barrier so that we read the current value) */
1245 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1246 return 0;
1247 }
1248
1249 session_count = ps->session_count;
1250
1251 /* UNLOCK */
1252 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1253
1254 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001255}
1256
Michal Vasko131120a2018-05-29 15:44:02 +02001257/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001258 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001259 * NC_PSPOLL_TIMEOUT,
Michal Vasko71090fc2016-05-24 16:37:28 +02001260 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1261 * NC_PSPOLL_RPC
1262 */
1263static int
Michal Vasko131120a2018-05-29 15:44:02 +02001264nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001265{
Michal Vasko77367452021-02-16 16:32:18 +01001266 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001267 struct nc_server_reply *reply = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001268 int r, ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001269
Michal Vasko45e53ae2016-04-07 11:46:03 +02001270 if (!session) {
1271 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001272 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001273 } else if (!rpc) {
1274 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001275 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001276 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001277 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001278 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001279 }
1280
Michal Vasko77367452021-02-16 16:32:18 +01001281 /* get a message */
1282 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1283 if (r == -2) {
1284 /* malformed message */
1285 ret = NC_PSPOLL_REPLY_ERROR;
1286 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_MALFORMED_MSG));
1287 goto send_reply;
1288 } if (r == -1) {
1289 return NC_PSPOLL_ERROR;
1290 } else if (!r) {
1291 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001292 }
1293
Michal Vasko77367452021-02-16 16:32:18 +01001294 *rpc = calloc(1, sizeof **rpc);
1295 if (!*rpc) {
1296 ERRMEM;
1297 ret = NC_PSPOLL_REPLY_ERROR;
1298 goto cleanup;
1299 }
1300
1301 /* parse the RPC */
1302 if (lyd_parse_op(server_opts.ctx, NULL, msg, LYD_XML, LYD_TYPE_RPC_NETCONF, &(*rpc)->envp, &(*rpc)->rpc)) {
1303 /* bad RPC received */
1304 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1305
1306 if ((*rpc)->envp) {
1307 /* at least the envelopes were parsed */
1308 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
1309 } else if (session->version == NC_VERSION_11) {
1310 /* completely malformed message, NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
1311 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_MALFORMED_MSG));
1312 }
1313
1314send_reply:
1315 if (reply) {
1316 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, (*rpc)->envp, reply);
1317 nc_server_reply_free(reply);
1318 if (r != NC_MSG_REPLY) {
1319 ERR("Session %u: failed to write reply (%s), terminating session.", session->id, nc_msgtype2str[r]);
1320 if (session->status != NC_STATUS_INVALID) {
1321 session->status = NC_STATUS_INVALID;
1322 session->term_reason = NC_SESSION_TERM_OTHER;
1323 }
1324 }
1325 }
1326 } else {
1327 ret = NC_PSPOLL_RPC;
1328 }
1329
1330cleanup:
1331 ly_in_free(msg, 1);
1332 if (ret != NC_PSPOLL_RPC) {
1333 nc_server_rpc_free(*rpc);
1334 *rpc = NULL;
1335 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001336 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001337}
1338
fanchanghu966f2de2016-07-21 02:28:57 -04001339API void
1340nc_set_global_rpc_clb(nc_rpc_clb clb)
1341{
1342 global_rpc_clb = clb;
1343}
1344
Radek Krejci93e80222016-10-03 13:34:25 +02001345API NC_MSG_TYPE
1346nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1347{
Michal Vasko131120a2018-05-29 15:44:02 +02001348 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001349
1350 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001351 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001352 ERRARG("session");
1353 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001354 } else if (!notif || !notif->ntf || !notif->eventtime) {
Radek Krejci93e80222016-10-03 13:34:25 +02001355 ERRARG("notif");
1356 return NC_MSG_ERROR;
1357 }
1358
Michal Vasko131120a2018-05-29 15:44:02 +02001359 /* we do not need RPC lock for this, IO lock will be acquired properly */
1360 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001361 if (ret != NC_MSG_NOTIF) {
1362 ERR("Session %u: failed to write notification (%s).", session->id, nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001363 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001364
Michal Vasko131120a2018-05-29 15:44:02 +02001365 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001366}
1367
Michal Vasko131120a2018-05-29 15:44:02 +02001368/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001369 * returns: NC_PSPOLL_ERROR,
1370 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1371 * NC_PSPOLL_REPLY_ERROR,
1372 * 0
1373 */
1374static int
Michal Vasko131120a2018-05-29 15:44:02 +02001375nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001376{
1377 nc_rpc_clb clb;
1378 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001379 const struct lysc_node *rpc_act = NULL;
1380 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001381 int ret = 0;
1382 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001383
Michal Vasko4a827e52016-03-03 10:59:00 +01001384 if (!rpc) {
1385 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001386 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001387 }
1388
Michal Vasko77367452021-02-16 16:32:18 +01001389 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001390 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001391 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001392 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001393 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001394 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001395 if (elem->schema->nodetype == LYS_ACTION) {
1396 rpc_act = elem->schema;
1397 break;
1398 }
Michal Vasko77367452021-02-16 16:32:18 +01001399 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001400 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001401 if (!rpc_act) {
1402 ERRINT;
1403 return NC_PSPOLL_ERROR;
1404 }
1405 }
1406
1407 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001408 if (!global_rpc_clb) {
1409 /* no callback, reply with a not-implemented error */
Michal Vasko77367452021-02-16 16:32:18 +01001410 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 +03001411 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001412 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001413 }
Michal Vasko428087d2016-01-14 16:04:28 +01001414 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001415 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001416 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001417 }
1418
1419 if (!reply) {
Michal Vasko77367452021-02-16 16:32:18 +01001420 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 +01001421 }
Michal Vasko77367452021-02-16 16:32:18 +01001422 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001423 if (reply->type == NC_RPL_ERROR) {
1424 ret |= NC_PSPOLL_REPLY_ERROR;
1425 }
1426 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001427
Michal Vasko131120a2018-05-29 15:44:02 +02001428 if (r != NC_MSG_REPLY) {
Michal Vasko8fe604c2020-02-10 15:25:04 +01001429 ERR("Session %u: failed to write reply (%s).", session->id, nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001430 ret |= NC_PSPOLL_ERROR;
1431 }
Michal Vasko428087d2016-01-14 16:04:28 +01001432
1433 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1434 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1435 session->status = NC_STATUS_INVALID;
1436 }
1437
Michal Vasko71090fc2016-05-24 16:37:28 +02001438 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001439}
1440
Michal Vasko131120a2018-05-29 15:44:02 +02001441/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001442 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1443 * NC_PSPOLL_ERROR, (msg filled)
1444 * NC_PSPOLL_TIMEOUT,
1445 * NC_PSPOLL_RPC (some application data available),
1446 * NC_PSPOLL_SSH_CHANNEL,
1447 * NC_PSPOLL_SSH_MSG
1448 */
1449static int
Michal Vasko131120a2018-05-29 15:44:02 +02001450nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001451{
Michal Vasko9a327362017-01-11 11:31:46 +01001452 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001453 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001454#ifdef NC_ENABLED_SSH
1455 struct nc_session *new;
1456#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001457
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001458 /* check timeout first */
1459 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001460 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001461 sprintf(msg, "session idle timeout elapsed");
1462 session->status = NC_STATUS_INVALID;
1463 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1464 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1465 }
1466
Michal Vasko131120a2018-05-29 15:44:02 +02001467 r = nc_session_io_lock(session, io_timeout, __func__);
1468 if (r < 0) {
1469 sprintf(msg, "session IO lock failed to be acquired");
1470 return NC_PSPOLL_ERROR;
1471 } else if (!r) {
1472 return NC_PSPOLL_TIMEOUT;
1473 }
1474
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001475 switch (session->ti_type) {
1476#ifdef NC_ENABLED_SSH
1477 case NC_TI_LIBSSH:
1478 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001479 if (r == SSH_EOF) {
1480 sprintf(msg, "SSH channel unexpected EOF");
1481 session->status = NC_STATUS_INVALID;
1482 session->term_reason = NC_SESSION_TERM_DROPPED;
1483 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1484 } else if (r == SSH_ERROR) {
1485 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001486 session->status = NC_STATUS_INVALID;
1487 session->term_reason = NC_SESSION_TERM_OTHER;
1488 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001489 } else if (!r) {
1490 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1491 /* new SSH message */
1492 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1493 if (session->ti.libssh.next) {
1494 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1495 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1496 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1497 /* new NETCONF SSH channel */
1498 ret = NC_PSPOLL_SSH_CHANNEL;
1499 break;
1500 }
1501 }
1502 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001503 break;
1504 }
1505 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001506
Michal Vasko8dcaa882017-10-19 14:28:42 +02001507 /* just some SSH message */
1508 ret = NC_PSPOLL_SSH_MSG;
1509 } else {
1510 ret = NC_PSPOLL_TIMEOUT;
1511 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001512 } else {
1513 /* we have some application data */
1514 ret = NC_PSPOLL_RPC;
1515 }
1516 break;
1517#endif
1518#ifdef NC_ENABLED_TLS
1519 case NC_TI_OPENSSL:
1520 r = SSL_pending(session->ti.tls);
1521 if (!r) {
1522 /* no data pending in the SSL buffer, poll fd */
1523 pfd.fd = SSL_get_rfd(session->ti.tls);
1524 if (pfd.fd < 0) {
1525 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1526 ret = NC_PSPOLL_ERROR;
1527 break;
1528 }
1529 pfd.events = POLLIN;
1530 pfd.revents = 0;
1531 r = poll(&pfd, 1, 0);
1532
1533 if ((r < 0) && (errno != EINTR)) {
1534 sprintf(msg, "poll failed (%s)", strerror(errno));
1535 session->status = NC_STATUS_INVALID;
1536 ret = NC_PSPOLL_ERROR;
1537 } else if (r > 0) {
1538 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1539 sprintf(msg, "communication socket unexpectedly closed");
1540 session->status = NC_STATUS_INVALID;
1541 session->term_reason = NC_SESSION_TERM_DROPPED;
1542 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1543 } else if (pfd.revents & POLLERR) {
1544 sprintf(msg, "communication socket error");
1545 session->status = NC_STATUS_INVALID;
1546 session->term_reason = NC_SESSION_TERM_OTHER;
1547 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1548 } else {
1549 ret = NC_PSPOLL_RPC;
1550 }
1551 } else {
1552 ret = NC_PSPOLL_TIMEOUT;
1553 }
1554 } else {
1555 ret = NC_PSPOLL_RPC;
1556 }
1557 break;
1558#endif
1559 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001560 case NC_TI_UNIX:
1561 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001562 pfd.events = POLLIN;
1563 pfd.revents = 0;
1564 r = poll(&pfd, 1, 0);
1565
1566 if ((r < 0) && (errno != EINTR)) {
1567 sprintf(msg, "poll failed (%s)", strerror(errno));
1568 session->status = NC_STATUS_INVALID;
1569 ret = NC_PSPOLL_ERROR;
1570 } else if (r > 0) {
1571 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1572 sprintf(msg, "communication socket unexpectedly closed");
1573 session->status = NC_STATUS_INVALID;
1574 session->term_reason = NC_SESSION_TERM_DROPPED;
1575 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1576 } else if (pfd.revents & POLLERR) {
1577 sprintf(msg, "communication socket error");
1578 session->status = NC_STATUS_INVALID;
1579 session->term_reason = NC_SESSION_TERM_OTHER;
1580 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1581 } else {
1582 ret = NC_PSPOLL_RPC;
1583 }
1584 } else {
1585 ret = NC_PSPOLL_TIMEOUT;
1586 }
1587 break;
1588 case NC_TI_NONE:
1589 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1590 ret = NC_PSPOLL_ERROR;
1591 break;
1592 }
1593
Michal Vasko131120a2018-05-29 15:44:02 +02001594 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001595 return ret;
1596}
1597
1598API int
1599nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1600{
1601 int ret, r;
1602 uint8_t q_id;
1603 uint16_t i, j;
1604 char msg[256];
1605 struct timespec ts_timeout, ts_cur;
1606 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001607 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001608 struct nc_server_rpc *rpc = NULL;
1609
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001610 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001611 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001612 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001613 }
1614
Michal Vaskoade892d2017-02-22 13:40:35 +01001615 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001616 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001617 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001618 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001619
Michal Vaskoade892d2017-02-22 13:40:35 +01001620 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001621 nc_ps_unlock(ps, q_id, __func__);
1622 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001623 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001624
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001625 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001626 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001627 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001628 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001629 nc_addtimespec(&ts_timeout, timeout);
1630 }
1631
1632 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001633 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001634 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001635 if (ps->last_event_session == ps->session_count - 1) {
1636 i = j = 0;
1637 } else {
1638 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001639 }
Michal Vasko9a327362017-01-11 11:31:46 +01001640 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001641 cur_ps_session = ps->sessions[i];
1642 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001643
Michal Vasko131120a2018-05-29 15:44:02 +02001644 /* SESSION RPC LOCK */
1645 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001646 if (r == -1) {
1647 ret = NC_PSPOLL_ERROR;
1648 } else if (r == 1) {
1649 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001650 switch (cur_ps_session->state) {
1651 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001652 if (cur_session->status == NC_STATUS_RUNNING) {
1653 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001654 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001655
Michal Vasko131120a2018-05-29 15:44:02 +02001656 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001657 switch (ret) {
1658 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1659 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001660 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001661 break;
1662 case NC_PSPOLL_ERROR:
1663 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001664 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001665 break;
1666 case NC_PSPOLL_TIMEOUT:
1667#ifdef NC_ENABLED_SSH
1668 case NC_PSPOLL_SSH_CHANNEL:
1669 case NC_PSPOLL_SSH_MSG:
1670#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001671 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001672 break;
1673 case NC_PSPOLL_RPC:
1674 /* let's keep the state busy, we are not done with this session */
1675 break;
1676 }
1677 } else {
1678 /* session is not fine, let the caller know */
1679 ret = NC_PSPOLL_SESSION_TERM;
1680 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1681 ret |= NC_PSPOLL_SESSION_ERROR;
1682 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001683 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001684 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001685 break;
1686 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001687 /* it definitely should not be busy because we have the lock */
1688 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001689 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001690 break;
1691 case NC_PS_STATE_INVALID:
1692 /* we got it locked, but it will be freed, let it be */
1693 ret = NC_PSPOLL_TIMEOUT;
1694 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001695 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001696
Michal Vasko131120a2018-05-29 15:44:02 +02001697 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001698 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001699 /* SESSION RPC UNLOCK */
1700 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001701 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001702 } else {
1703 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001704 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001705 }
Michal Vasko428087d2016-01-14 16:04:28 +01001706
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001707 /* something happened */
1708 if (ret != NC_PSPOLL_TIMEOUT) {
1709 break;
1710 }
1711
Michal Vasko9a327362017-01-11 11:31:46 +01001712 if (i == ps->session_count - 1) {
1713 i = 0;
1714 } else {
1715 ++i;
1716 }
1717 } while (i != j);
1718
Michal Vaskoade892d2017-02-22 13:40:35 +01001719 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001720 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001721 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001722 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001723 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001724
1725 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1726 /* final timeout */
1727 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001728 }
Michal Vasko428087d2016-01-14 16:04:28 +01001729 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001730 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001731
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001732 /* do we want to return the session? */
1733 switch (ret) {
1734 case NC_PSPOLL_RPC:
1735 case NC_PSPOLL_SESSION_TERM:
1736 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1737#ifdef NC_ENABLED_SSH
1738 case NC_PSPOLL_SSH_CHANNEL:
1739 case NC_PSPOLL_SSH_MSG:
1740#endif
1741 if (session) {
1742 *session = cur_session;
1743 }
1744 ps->last_event_session = i;
1745 break;
1746 default:
1747 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001748 }
Michal Vasko428087d2016-01-14 16:04:28 +01001749
Michal Vaskoade892d2017-02-22 13:40:35 +01001750 /* PS UNLOCK */
1751 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001752
Michal Vasko131120a2018-05-29 15:44:02 +02001753 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001754 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001755 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001756 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1757 if (cur_session->status != NC_STATUS_RUNNING) {
1758 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001759 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001760 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001761 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001762 }
1763 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001764 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001765
Michal Vasko7f1ee932018-10-11 09:41:42 +02001766 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001767 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001768 if (cur_session->status != NC_STATUS_RUNNING) {
1769 ret |= NC_PSPOLL_SESSION_TERM;
1770 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1771 ret |= NC_PSPOLL_SESSION_ERROR;
1772 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001773 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001774 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001775 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001776 }
Michal Vasko428087d2016-01-14 16:04:28 +01001777 }
Michal Vasko77367452021-02-16 16:32:18 +01001778 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001779
Michal Vasko131120a2018-05-29 15:44:02 +02001780 /* SESSION RPC UNLOCK */
1781 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001782 }
1783
Michal Vasko48a63ed2016-03-01 09:48:21 +01001784 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001785}
1786
Michal Vaskod09eae62016-02-01 10:32:52 +01001787API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001788nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001789{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001790 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001791 uint16_t i;
1792 struct nc_session *session;
1793
Michal Vasko9a25e932016-02-01 10:36:42 +01001794 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001795 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001796 return;
1797 }
1798
Michal Vasko48a63ed2016-03-01 09:48:21 +01001799 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001800 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001801 return;
1802 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001803
Michal Vasko48a63ed2016-03-01 09:48:21 +01001804 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001805 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001806 nc_session_free(ps->sessions[i]->session, data_free);
1807 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001808 }
1809 free(ps->sessions);
1810 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001811 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001812 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001813 } else {
1814 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001815 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1816 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001817 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001818 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001819 continue;
1820 }
1821
1822 ++i;
1823 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001824 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001825
1826 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001827 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001828}
1829
Michal Vasko5f352c52019-07-10 16:12:06 +02001830static int
apropp-molex4e903c32020-04-20 03:06:58 -04001831nc_get_uid(int sock, uid_t *uid)
1832{
Michal Vaskod3910912020-04-20 09:12:49 +02001833 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04001834
Michal Vaskod3910912020-04-20 09:12:49 +02001835#ifdef SO_PEERCRED
1836 struct ucred ucred;
1837 socklen_t len;
1838 len = sizeof(ucred);
1839 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
1840 if (!ret) {
1841 *uid = ucred.uid;
1842 }
1843#else
1844 ret = getpeereid(sock, uid, NULL);
1845#endif
1846
1847 if (ret < 0) {
1848 ERR("Failed to get credentials from unix socket (%s).", strerror(errno));
1849 return -1;
1850 }
apropp-molex4e903c32020-04-20 03:06:58 -04001851 return 0;
1852}
1853
1854static int
Michal Vasko5f352c52019-07-10 16:12:06 +02001855nc_accept_unix(struct nc_session *session, int sock)
1856{
apropp-molex4e903c32020-04-20 03:06:58 -04001857#if defined(SO_PEERCRED) || defined(HAVE_GETPEEREID)
Michal Vasko5f352c52019-07-10 16:12:06 +02001858 const struct passwd *pw;
Michal Vasko5f352c52019-07-10 16:12:06 +02001859 char *username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001860 session->ti_type = NC_TI_UNIX;
apropp-molex4e903c32020-04-20 03:06:58 -04001861 uid_t uid;
Michal Vasko5f352c52019-07-10 16:12:06 +02001862
Michal Vaskod3910912020-04-20 09:12:49 +02001863 if (nc_get_uid(sock, &uid)) {
1864 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02001865 return -1;
1866 }
1867
apropp-molex4e903c32020-04-20 03:06:58 -04001868 pw = getpwuid(uid);
Michal Vasko5f352c52019-07-10 16:12:06 +02001869 if (pw == NULL) {
Michal Vasko77367452021-02-16 16:32:18 +01001870 ERR("Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02001871 close(sock);
1872 return -1;
1873 }
1874
1875 username = strdup(pw->pw_name);
1876 if (username == NULL) {
1877 ERRMEM;
1878 close(sock);
1879 return -1;
1880 }
Michal Vasko77367452021-02-16 16:32:18 +01001881 lydict_insert_zc(server_opts.ctx, username, &session->username);
Michal Vasko5f352c52019-07-10 16:12:06 +02001882
1883 session->ti.unixsock.sock = sock;
1884
1885 return 1;
Claus Klein22091912020-01-20 13:45:47 +01001886#else
1887 return -1;
1888#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02001889}
1890
Michal Vaskoe2713da2016-08-22 16:06:40 +02001891API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001892nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001893{
Michal Vasko3031aae2016-01-27 16:07:18 +01001894 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001895 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001896
Michal Vasko45e53ae2016-04-07 11:46:03 +02001897 if (!name) {
1898 ERRARG("name");
1899 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001900 }
1901
Michal Vaskoade892d2017-02-22 13:40:35 +01001902 /* BIND LOCK */
1903 pthread_mutex_lock(&server_opts.bind_lock);
1904
1905 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001906 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001907
1908 /* check name uniqueness */
1909 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001910 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001911 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001912 ret = -1;
1913 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001914 }
1915 }
1916
Michal Vasko3031aae2016-01-27 16:07:18 +01001917 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001918 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001919 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001920 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001921 ret = -1;
1922 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001923 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001924 memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
Michal Vasko77367452021-02-16 16:32:18 +01001925 lydict_insert(server_opts.ctx, name, 0, &server_opts.endpts[server_opts.endpt_count - 1].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001926 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001927 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1928 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1929 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001930
Michal Vaskoe2713da2016-08-22 16:06:40 +02001931 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001932 if (!server_opts.binds) {
1933 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001934 ret = -1;
1935 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001936 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001937
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001938 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001939 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1940
1941 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001942#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001943 case NC_TI_LIBSSH:
1944 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1945 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1946 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001947 ret = -1;
1948 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001949 }
1950 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
Michal Vasko77367452021-02-16 16:32:18 +01001951 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001952 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02001953 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001954 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001955#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001956#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001957 case NC_TI_OPENSSL:
1958 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1959 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1960 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001961 ret = -1;
1962 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001963 }
1964 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001965#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001966 case NC_TI_UNIX:
1967 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1968 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1969 ERRMEM;
1970 ret = -1;
1971 goto cleanup;
1972 }
1973 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1974 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1975 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1976 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001977 default:
1978 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001979 ret = -1;
1980 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001981 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001982
Michal Vaskoade892d2017-02-22 13:40:35 +01001983cleanup:
1984 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001985 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001986
Michal Vaskoade892d2017-02-22 13:40:35 +01001987 /* BIND UNLOCK */
1988 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001989
Michal Vaskoade892d2017-02-22 13:40:35 +01001990 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001991}
1992
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001993API int
Michal Vasko59050372016-11-22 14:33:55 +01001994nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001995{
1996 uint32_t i;
1997 int ret = -1;
1998
Michal Vaskoade892d2017-02-22 13:40:35 +01001999 /* BIND LOCK */
2000 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002001
Michal Vaskoade892d2017-02-22 13:40:35 +01002002 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002003 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002004
Michal Vasko59050372016-11-22 14:33:55 +01002005 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002006 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01002007 for (i = 0; i < server_opts.endpt_count; ++i) {
2008 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002009 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002010#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002011 case NC_TI_LIBSSH:
2012 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2013 free(server_opts.endpts[i].opts.ssh);
2014 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002015#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002016#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002017 case NC_TI_OPENSSL:
2018 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2019 free(server_opts.endpts[i].opts.tls);
2020 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002021#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002022 case NC_TI_UNIX:
2023 free(server_opts.endpts[i].opts.unixsock);
2024 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002025 default:
2026 ERRINT;
2027 /* won't get here ...*/
2028 break;
2029 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002030 ret = 0;
2031 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002032 free(server_opts.endpts);
2033 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002034
2035 /* remove all binds */
2036 for (i = 0; i < server_opts.endpt_count; ++i) {
2037 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2038 if (server_opts.binds[i].sock > -1) {
2039 close(server_opts.binds[i].sock);
2040 }
2041 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002042 free(server_opts.binds);
2043 server_opts.binds = NULL;
2044
Michal Vasko3031aae2016-01-27 16:07:18 +01002045 server_opts.endpt_count = 0;
2046
Michal Vasko1a38c862016-01-15 15:50:07 +01002047 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002048 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002049 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002050 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002051 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01002052 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002053 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002054#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002055 case NC_TI_LIBSSH:
2056 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2057 free(server_opts.endpts[i].opts.ssh);
2058 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002059#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002060#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002061 case NC_TI_OPENSSL:
2062 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2063 free(server_opts.endpts[i].opts.tls);
2064 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002065#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002066 case NC_TI_UNIX:
2067 free(server_opts.endpts[i].opts.unixsock);
2068 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002069 default:
2070 ERRINT;
2071 break;
2072 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002073
Michal Vaskoe2713da2016-08-22 16:06:40 +02002074 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02002075 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2076 if (server_opts.binds[i].sock > -1) {
2077 close(server_opts.binds[i].sock);
2078 }
2079
2080 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002081 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002082 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002083 free(server_opts.binds);
2084 server_opts.binds = NULL;
2085 free(server_opts.endpts);
2086 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002087 } else if (i < server_opts.endpt_count) {
2088 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2089 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002090 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002091
2092 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002093 if (name) {
2094 break;
2095 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002096 }
2097 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002098 }
2099
Michal Vaskoade892d2017-02-22 13:40:35 +01002100 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002101 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002102
Michal Vaskoade892d2017-02-22 13:40:35 +01002103 /* BIND UNLOCK */
2104 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002105
2106 return ret;
2107}
2108
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002109API int
2110nc_server_endpt_count(void)
2111{
2112 return server_opts.endpt_count;
2113}
2114
Michal Vasko1b5973e2020-01-30 16:05:46 +01002115API int
2116nc_server_is_endpt(const char *name)
2117{
2118 uint16_t i;
2119 int found = 0;
2120
Michal Vaskofb1724b2020-01-31 11:02:00 +01002121 if (!name) {
2122 return found;
2123 }
2124
Michal Vasko1b5973e2020-01-30 16:05:46 +01002125 /* ENDPT READ LOCK */
2126 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2127
2128 /* check name uniqueness */
2129 for (i = 0; i < server_opts.endpt_count; ++i) {
2130 if (!strcmp(server_opts.endpts[i].name, name)) {
2131 found = 1;
2132 break;
2133 }
2134 }
2135
2136 /* ENDPT UNLOCK */
2137 pthread_rwlock_unlock(&server_opts.endpt_lock);
2138
2139 return found;
2140}
2141
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002142int
2143nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2144{
2145 struct nc_endpt *endpt;
2146 struct nc_bind *bind = NULL;
2147 uint16_t i;
2148 int sock = -1, set_addr, ret = 0;
2149
2150 if (!endpt_name) {
2151 ERRARG("endpt_name");
2152 return -1;
2153 } else if ((!address && !port) || (address && port)) {
2154 ERRARG("address and port");
2155 return -1;
2156 }
2157
2158 if (address) {
2159 set_addr = 1;
2160 } else {
2161 set_addr = 0;
2162 }
2163
2164 /* BIND LOCK */
2165 pthread_mutex_lock(&server_opts.bind_lock);
2166
2167 /* ENDPT LOCK */
2168 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2169 if (!endpt) {
2170 /* BIND UNLOCK */
2171 pthread_mutex_unlock(&server_opts.bind_lock);
2172 return -1;
2173 }
2174
2175 bind = &server_opts.binds[i];
2176
2177 if (set_addr) {
2178 port = bind->port;
2179 } else {
2180 address = bind->address;
2181 }
2182
2183 if (!set_addr && endpt->ti == NC_TI_UNIX) {
2184 ret = -1;
2185 goto cleanup;
2186 }
2187
2188 /* we have all the information we need to create a listening socket */
2189 if (address && (port || endpt->ti == NC_TI_UNIX)) {
2190 /* create new socket, close the old one */
2191 if (endpt->ti == NC_TI_UNIX)
2192 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
2193 else
2194 sock = nc_sock_listen_inet(address, port, &endpt->ka);
2195 if (sock == -1) {
2196 ret = -1;
2197 goto cleanup;
2198 }
2199
2200 if (bind->sock > -1) {
2201 close(bind->sock);
2202 }
2203 bind->sock = sock;
2204 } /* else we are just setting address or port */
2205
2206 if (set_addr) {
2207 lydict_remove(server_opts.ctx, bind->address);
Michal Vasko77367452021-02-16 16:32:18 +01002208 lydict_insert(server_opts.ctx, address, 0, &bind->address);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002209 } else {
2210 bind->port = port;
2211 }
2212
2213 if (sock > -1) {
Michal Vasko946cacb2020-08-12 11:18:08 +02002214 switch (endpt->ti) {
2215 case NC_TI_UNIX:
2216 VRB("Listening on %s for UNIX connections.", address);
2217 break;
2218#ifdef NC_ENABLED_SSH
2219 case NC_TI_LIBSSH:
2220 VRB("Listening on %s:%u for SSH connections.", address, port);
2221 break;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002222#endif
Michal Vasko946cacb2020-08-12 11:18:08 +02002223#ifdef NC_ENABLED_TLS
2224 case NC_TI_OPENSSL:
2225 VRB("Listening on %s:%u for TLS connections.", address, port);
2226 break;
2227#endif
2228 default:
2229 ERRINT;
2230 break;
2231 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002232 }
2233
2234cleanup:
2235 /* ENDPT UNLOCK */
2236 pthread_rwlock_unlock(&server_opts.endpt_lock);
2237
2238 /* BIND UNLOCK */
2239 pthread_mutex_unlock(&server_opts.bind_lock);
2240
2241 return ret;
2242}
2243
2244API int
2245nc_server_endpt_set_address(const char *endpt_name, const char *address)
2246{
2247 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2248}
2249
Michal Vasko946cacb2020-08-12 11:18:08 +02002250#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
2251
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002252API int
2253nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2254{
2255 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2256}
2257
Michal Vasko946cacb2020-08-12 11:18:08 +02002258#endif
2259
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002260API int
2261nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2262{
2263 struct nc_endpt *endpt;
2264 uint16_t i;
2265 int ret = 0;
2266
2267 if (!endpt_name) {
2268 ERRARG("endpt_name");
2269 return -1;
2270 } else if (mode == 0) {
2271 ERRARG("mode");
2272 return -1;
2273 }
2274
2275 /* ENDPT LOCK */
2276 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2277 if (!endpt)
2278 return -1;
2279
2280 if (endpt->ti != NC_TI_UNIX) {
2281 ret = -1;
2282 goto cleanup;
2283 }
2284
2285 endpt->opts.unixsock->mode = mode;
2286 endpt->opts.unixsock->uid = uid;
2287 endpt->opts.unixsock->gid = gid;
2288
2289cleanup:
2290 /* ENDPT UNLOCK */
2291 pthread_rwlock_unlock(&server_opts.endpt_lock);
2292
2293 return ret;
2294}
2295
2296API int
2297nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2298{
2299 struct nc_endpt *endpt;
2300 int ret = 0;
2301
2302 if (!endpt_name) {
2303 ERRARG("endpt_name");
2304 return -1;
2305 }
2306
2307 /* ENDPT LOCK */
2308 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2309 if (!endpt) {
2310 return -1;
2311 }
2312
2313 endpt->ka.enabled = (enable ? 1 : 0);
2314
2315 /* ENDPT UNLOCK */
2316 pthread_rwlock_unlock(&server_opts.endpt_lock);
2317
2318 return ret;
2319}
2320
2321API int
2322nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2323{
2324 struct nc_endpt *endpt;
2325 int ret = 0;
2326
2327 if (!endpt_name) {
2328 ERRARG("endpt_name");
2329 return -1;
2330 }
2331
2332 /* ENDPT LOCK */
2333 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2334 if (!endpt) {
2335 return -1;
2336 }
2337
2338 if (idle_time > -1) {
2339 endpt->ka.idle_time = idle_time;
2340 }
2341 if (max_probes > -1) {
2342 endpt->ka.max_probes = max_probes;
2343 }
2344 if (probe_interval > -1) {
2345 endpt->ka.probe_interval = probe_interval;
2346 }
2347
2348 /* ENDPT UNLOCK */
2349 pthread_rwlock_unlock(&server_opts.endpt_lock);
2350
2351 return ret;
2352}
2353
Michal Vasko71090fc2016-05-24 16:37:28 +02002354API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002355nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002356{
Michal Vasko71090fc2016-05-24 16:37:28 +02002357 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002358 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002359 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002360 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002361 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002362
Michal Vasko45e53ae2016-04-07 11:46:03 +02002363 if (!server_opts.ctx) {
2364 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002365 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002366 } else if (!session) {
2367 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002368 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002369 }
2370
Michal Vaskoade892d2017-02-22 13:40:35 +01002371 /* BIND LOCK */
2372 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002373
2374 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002375 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002376 /* BIND UNLOCK */
2377 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002378 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002379 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002380
Michal Vaskoe2713da2016-08-22 16:06:40 +02002381 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002382 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002383 /* BIND UNLOCK */
2384 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002385 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002386 if (!ret) {
2387 return NC_MSG_WOULDBLOCK;
2388 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002389 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002390 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002391
2392 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2393 /* ENDPT READ LOCK */
2394 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2395
2396 /* BIND UNLOCK */
2397 pthread_mutex_unlock(&server_opts.bind_lock);
2398
Michal Vaskob48aa812016-01-18 14:13:09 +01002399 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002400
Michal Vasko131120a2018-05-29 15:44:02 +02002401 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002402 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002403 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002404 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002405 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002406 msgtype = NC_MSG_ERROR;
2407 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002408 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002409 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002410 (*session)->ctx = server_opts.ctx;
2411 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko77367452021-02-16 16:32:18 +01002412 lydict_insert_zc(server_opts.ctx, host, &(*session)->host);
Michal Vasko1a38c862016-01-15 15:50:07 +01002413 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002414
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002415 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002416#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002417 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2418 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002419 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002420 if (ret < 0) {
2421 msgtype = NC_MSG_ERROR;
2422 goto cleanup;
2423 } else if (!ret) {
2424 msgtype = NC_MSG_WOULDBLOCK;
2425 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002426 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002427 } else
2428#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002429#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002430 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2431 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002432 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002433 if (ret < 0) {
2434 msgtype = NC_MSG_ERROR;
2435 goto cleanup;
2436 } else if (!ret) {
2437 msgtype = NC_MSG_WOULDBLOCK;
2438 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002439 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002440 } else
2441#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002442 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2443 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2444 ret = nc_accept_unix(*session, sock);
2445 if (ret < 0) {
2446 msgtype = NC_MSG_ERROR;
2447 goto cleanup;
2448 }
2449 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002450 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002451 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002452 msgtype = NC_MSG_ERROR;
2453 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002454 }
2455
Michal Vasko2cc4c682016-03-01 09:16:48 +01002456 (*session)->data = NULL;
2457
Michal Vaskoade892d2017-02-22 13:40:35 +01002458 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002459 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002460
Michal Vaskob48aa812016-01-18 14:13:09 +01002461 /* assign new SID atomically */
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002462 (*session)->id = ATOMIC_INC(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002463
Michal Vasko9e036d52016-01-08 10:49:26 +01002464 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002465 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002466 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002467 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002468 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002469 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002470 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002471
2472 nc_gettimespec_mono(&ts_cur);
2473 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2474 nc_gettimespec_real(&ts_cur);
2475 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002476 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002477
Michal Vasko71090fc2016-05-24 16:37:28 +02002478 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002479
Michal Vasko71090fc2016-05-24 16:37:28 +02002480cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002481 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002482 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002483
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002484 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002485 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002486 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002487}
2488
Michal Vasko946cacb2020-08-12 11:18:08 +02002489#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
2490
Michal Vaskoadf30f02019-06-24 09:34:47 +02002491/* client is expected to be locked */
2492static int
2493_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 +02002494{
2495 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002496 int ret = -1;
2497
2498 if (!endpt_name) {
2499 /* remove all endpoints */
2500 for (i = 0; i < client->ch_endpt_count; ++i) {
2501 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2502 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2503 if (client->ch_endpts[i].sock_pending != -1) {
2504 close(client->ch_endpts[i].sock_pending);
2505 }
2506 switch (client->ch_endpts[i].ti) {
2507#ifdef NC_ENABLED_SSH
2508 case NC_TI_LIBSSH:
2509 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2510 free(client->ch_endpts[i].opts.ssh);
2511 break;
2512#endif
2513#ifdef NC_ENABLED_TLS
2514 case NC_TI_OPENSSL:
2515 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2516 free(client->ch_endpts[i].opts.tls);
2517 break;
2518#endif
2519 default:
2520 ERRINT;
2521 /* won't get here ...*/
2522 break;
2523 }
2524 }
2525 free(client->ch_endpts);
2526 client->ch_endpts = NULL;
2527 client->ch_endpt_count = 0;
2528
2529 ret = 0;
2530 } else {
2531 for (i = 0; i < client->ch_endpt_count; ++i) {
2532 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
2533 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2534 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2535 if (client->ch_endpts[i].sock_pending != -1) {
2536 close(client->ch_endpts[i].sock_pending);
2537 }
2538 switch (client->ch_endpts[i].ti) {
2539#ifdef NC_ENABLED_SSH
2540 case NC_TI_LIBSSH:
2541 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2542 free(client->ch_endpts[i].opts.ssh);
2543 break;
2544#endif
2545#ifdef NC_ENABLED_TLS
2546 case NC_TI_OPENSSL:
2547 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2548 free(client->ch_endpts[i].opts.tls);
2549 break;
2550#endif
2551 default:
2552 ERRINT;
2553 /* won't get here ...*/
2554 break;
2555 }
2556
2557 /* move last endpoint to the empty space */
2558 --client->ch_endpt_count;
2559 if (i < client->ch_endpt_count) {
2560 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2561 } else if (!server_opts.ch_client_count) {
2562 free(server_opts.ch_clients);
2563 server_opts.ch_clients = NULL;
2564 }
2565
2566 ret = 0;
2567 break;
2568 }
2569 }
2570 }
2571
2572 return ret;
2573}
2574
2575API int
2576nc_server_ch_add_client(const char *name)
2577{
2578 uint16_t i;
2579 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002580
2581 if (!name) {
2582 ERRARG("name");
2583 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002584 }
2585
2586 /* WRITE LOCK */
2587 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2588
2589 /* check name uniqueness */
2590 for (i = 0; i < server_opts.ch_client_count; ++i) {
2591 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2592 ERR("Call Home client \"%s\" already exists.", name);
2593 /* WRITE UNLOCK */
2594 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2595 return -1;
2596 }
2597 }
2598
2599 ++server_opts.ch_client_count;
2600 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2601 if (!server_opts.ch_clients) {
2602 ERRMEM;
2603 /* WRITE UNLOCK */
2604 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2605 return -1;
2606 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002607 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002608
Michal Vasko77367452021-02-16 16:32:18 +01002609 lydict_insert(server_opts.ctx, name, 0, &client->name);
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002610 client->id = ATOMIC_INC(server_opts.new_client_id);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002611 client->ch_endpts = NULL;
2612 client->ch_endpt_count = 0;
2613 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002614
Michal Vasko2e6defd2016-10-07 15:48:15 +02002615 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002616 client->start_with = NC_CH_FIRST_LISTED;
2617 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002618
Michal Vaskoadf30f02019-06-24 09:34:47 +02002619 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002620
2621 /* WRITE UNLOCK */
2622 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2623
2624 return 0;
2625}
2626
2627API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002628nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002629{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002630 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002631 int ret = -1;
2632
2633 /* WRITE LOCK */
2634 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2635
Michal Vaskoadf30f02019-06-24 09:34:47 +02002636 if (!name) {
2637 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002638 for (i = 0; i < server_opts.ch_client_count; ++i) {
2639 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2640
2641 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002642 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002643
2644 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002645 ret = 0;
2646 }
2647 free(server_opts.ch_clients);
2648 server_opts.ch_clients = NULL;
2649
2650 server_opts.ch_client_count = 0;
2651
2652 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002653 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002654 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002655 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002656 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2657
Michal Vasko2e6defd2016-10-07 15:48:15 +02002658 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002659 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002660
2661 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2662
2663 /* move last client and endpoint(s) to the empty space */
2664 --server_opts.ch_client_count;
2665 if (i < server_opts.ch_client_count) {
2666 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002667 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002668 } else if (!server_opts.ch_client_count) {
2669 free(server_opts.ch_clients);
2670 server_opts.ch_clients = NULL;
2671 }
2672
2673 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002674 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002675 }
2676 }
2677 }
2678
2679 /* WRITE UNLOCK */
2680 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2681
2682 return ret;
2683}
2684
2685API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002686nc_server_ch_is_client(const char *name)
2687{
2688 uint16_t i;
2689 int found = 0;
2690
2691 if (!name) {
2692 return found;
2693 }
2694
2695 /* READ LOCK */
2696 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2697
2698 /* check name uniqueness */
2699 for (i = 0; i < server_opts.ch_client_count; ++i) {
2700 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2701 found = 1;
2702 break;
2703 }
2704 }
2705
2706 /* UNLOCK */
2707 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2708
2709 return found;
2710}
2711
2712API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002713nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002714{
2715 uint16_t i;
2716 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002717 struct nc_ch_endpt *endpt;
2718 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002719
2720 if (!client_name) {
2721 ERRARG("client_name");
2722 return -1;
2723 } else if (!endpt_name) {
2724 ERRARG("endpt_name");
2725 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002726 } else if (!ti) {
2727 ERRARG("ti");
2728 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002729 }
2730
2731 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002732 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002733 if (!client) {
2734 return -1;
2735 }
2736
2737 for (i = 0; i < client->ch_endpt_count; ++i) {
2738 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2739 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002740 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002741 }
2742 }
2743
2744 ++client->ch_endpt_count;
2745 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2746 if (!client->ch_endpts) {
2747 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002748 goto cleanup;
2749 }
2750 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2751
2752 memset(endpt, 0, sizeof *client->ch_endpts);
Michal Vasko77367452021-02-16 16:32:18 +01002753 lydict_insert(server_opts.ctx, endpt_name, 0, &endpt->name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002754 endpt->ti = ti;
2755 endpt->sock_pending = -1;
2756 endpt->ka.idle_time = 1;
2757 endpt->ka.max_probes = 10;
2758 endpt->ka.probe_interval = 5;
2759
2760 switch (ti) {
2761#ifdef NC_ENABLED_SSH
2762 case NC_TI_LIBSSH:
2763 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2764 if (!endpt->opts.ssh) {
2765 ERRMEM;
2766 goto cleanup;
2767 }
2768 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2769 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002770 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002771 break;
2772#endif
2773#ifdef NC_ENABLED_TLS
2774 case NC_TI_OPENSSL:
2775 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2776 if (!endpt->opts.tls) {
2777 ERRMEM;
2778 goto cleanup;
2779 }
2780 break;
2781#endif
2782 default:
2783 ERRINT;
2784 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002785 }
2786
Michal Vaskoadf30f02019-06-24 09:34:47 +02002787 /* success */
2788 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002789
Michal Vaskoadf30f02019-06-24 09:34:47 +02002790cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002791 /* UNLOCK */
2792 nc_server_ch_client_unlock(client);
2793
Michal Vaskoadf30f02019-06-24 09:34:47 +02002794 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002795}
2796
2797API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002798nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002799{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002800 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002801 struct nc_ch_client *client;
2802
2803 if (!client_name) {
2804 ERRARG("client_name");
2805 return -1;
2806 }
2807
2808 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002809 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002810 if (!client) {
2811 return -1;
2812 }
2813
Michal Vaskoadf30f02019-06-24 09:34:47 +02002814 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002815
2816 /* UNLOCK */
2817 nc_server_ch_client_unlock(client);
2818
2819 return ret;
2820}
2821
2822API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002823nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2824{
2825 uint16_t i;
2826 struct nc_ch_client *client = NULL;
2827 int found = 0;
2828
2829 if (!client_name || !endpt_name) {
2830 return found;
2831 }
2832
2833 /* READ LOCK */
2834 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2835
2836 for (i = 0; i < server_opts.ch_client_count; ++i) {
2837 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2838 client = &server_opts.ch_clients[i];
2839 break;
2840 }
2841 }
2842
2843 if (!client) {
2844 goto cleanup;
2845 }
2846
2847 for (i = 0; i < client->ch_endpt_count; ++i) {
2848 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2849 found = 1;
2850 break;
2851 }
2852 }
2853
2854cleanup:
2855 /* UNLOCK */
2856 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2857 return found;
2858}
2859
2860API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002861nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2862{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002863 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002864 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002865
2866 if (!client_name) {
2867 ERRARG("client_name");
2868 return -1;
2869 } else if (!endpt_name) {
2870 ERRARG("endpt_name");
2871 return -1;
2872 } else if (!address) {
2873 ERRARG("address");
2874 return -1;
2875 }
2876
2877 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002878 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2879 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002880 return -1;
2881 }
2882
Michal Vaskoadf30f02019-06-24 09:34:47 +02002883 lydict_remove(server_opts.ctx, endpt->address);
Michal Vasko77367452021-02-16 16:32:18 +01002884 lydict_insert(server_opts.ctx, address, 0, &endpt->address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002885
2886 /* UNLOCK */
2887 nc_server_ch_client_unlock(client);
2888
Michal Vaskoadf30f02019-06-24 09:34:47 +02002889 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002890}
2891
2892API int
2893nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2894{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002895 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002896 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002897
2898 if (!client_name) {
2899 ERRARG("client_name");
2900 return -1;
2901 } else if (!endpt_name) {
2902 ERRARG("endpt_name");
2903 return -1;
2904 } else if (!port) {
2905 ERRARG("port");
2906 return -1;
2907 }
2908
2909 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002910 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2911 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002912 return -1;
2913 }
2914
Michal Vaskoadf30f02019-06-24 09:34:47 +02002915 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002916
2917 /* UNLOCK */
2918 nc_server_ch_client_unlock(client);
2919
ravsz5c5a4422020-03-31 15:53:21 +02002920 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002921}
2922
2923API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002924nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2925{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002926 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002927 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002928
2929 if (!client_name) {
2930 ERRARG("client_name");
2931 return -1;
2932 } else if (!endpt_name) {
2933 ERRARG("endpt_name");
2934 return -1;
2935 }
2936
2937 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002938 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2939 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002940 return -1;
2941 }
2942
Michal Vaskoadf30f02019-06-24 09:34:47 +02002943 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002944
2945 /* UNLOCK */
2946 nc_server_ch_client_unlock(client);
2947
Michal Vasko9af829a2019-09-12 13:50:00 +02002948 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002949}
2950
2951API int
2952nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
2953 int probe_interval)
2954{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002955 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002956 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002957
2958 if (!client_name) {
2959 ERRARG("client_name");
2960 return -1;
2961 } else if (!endpt_name) {
2962 ERRARG("endpt_name");
2963 return -1;
2964 }
2965
2966 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002967 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2968 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002969 return -1;
2970 }
2971
Michal Vaskoadf30f02019-06-24 09:34:47 +02002972 if (idle_time > -1) {
2973 endpt->ka.idle_time = idle_time;
2974 }
2975 if (max_probes > -1) {
2976 endpt->ka.max_probes = max_probes;
2977 }
2978 if (probe_interval > -1) {
2979 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002980 }
2981
2982 /* UNLOCK */
2983 nc_server_ch_client_unlock(client);
2984
Michal Vasko9af829a2019-09-12 13:50:00 +02002985 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002986}
2987
2988API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002989nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2990{
2991 struct nc_ch_client *client;
2992
2993 if (!client_name) {
2994 ERRARG("client_name");
2995 return -1;
2996 } else if (!conn_type) {
2997 ERRARG("conn_type");
2998 return -1;
2999 }
3000
3001 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003002 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003003 if (!client) {
3004 return -1;
3005 }
3006
3007 if (client->conn_type != conn_type) {
3008 client->conn_type = conn_type;
3009
3010 /* set default options */
3011 switch (conn_type) {
3012 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003013 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003014 break;
3015 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003016 client->conn.period.period = 60;
3017 client->conn.period.anchor_time = 0;
3018 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003019 break;
3020 default:
3021 ERRINT;
3022 break;
3023 }
3024 }
3025
3026 /* UNLOCK */
3027 nc_server_ch_client_unlock(client);
3028
3029 return 0;
3030}
3031
3032API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003033nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
3034{
3035 struct nc_ch_client *client;
3036
3037 if (!client_name) {
3038 ERRARG("client_name");
3039 return -1;
3040 } else if (!period) {
3041 ERRARG("period");
3042 return -1;
3043 }
3044
3045 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003046 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003047 if (!client) {
3048 return -1;
3049 }
3050
3051 if (client->conn_type != NC_CH_PERIOD) {
ravsz20789e12020-03-31 14:43:05 +02003052 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003053 /* UNLOCK */
3054 nc_server_ch_client_unlock(client);
3055 return -1;
3056 }
3057
3058 client->conn.period.period = period;
3059
3060 /* UNLOCK */
3061 nc_server_ch_client_unlock(client);
3062
3063 return 0;
3064}
3065
3066API int
3067nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003068{
3069 struct nc_ch_client *client;
3070
3071 if (!client_name) {
3072 ERRARG("client_name");
3073 return -1;
3074 }
3075
3076 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003077 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003078 if (!client) {
3079 return -1;
3080 }
3081
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003082 if (client->conn_type != NC_CH_PERIOD) {
ravsz20789e12020-03-31 14:43:05 +02003083 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003084 /* UNLOCK */
3085 nc_server_ch_client_unlock(client);
3086 return -1;
3087 }
3088
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003089 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003090
3091 /* UNLOCK */
3092 nc_server_ch_client_unlock(client);
3093
3094 return 0;
3095}
3096
3097API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003098nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003099{
3100 struct nc_ch_client *client;
3101
3102 if (!client_name) {
3103 ERRARG("client_name");
3104 return -1;
3105 }
3106
3107 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003108 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003109 if (!client) {
3110 return -1;
3111 }
3112
3113 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10003114 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003115 /* UNLOCK */
3116 nc_server_ch_client_unlock(client);
3117 return -1;
3118 }
3119
3120 client->conn.period.idle_timeout = idle_timeout;
3121
3122 /* UNLOCK */
3123 nc_server_ch_client_unlock(client);
3124
3125 return 0;
3126}
3127
3128API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003129nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
3130{
3131 struct nc_ch_client *client;
3132
3133 if (!client_name) {
3134 ERRARG("client_name");
3135 return -1;
3136 }
3137
3138 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003139 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003140 if (!client) {
3141 return -1;
3142 }
3143
3144 client->start_with = start_with;
3145
3146 /* UNLOCK */
3147 nc_server_ch_client_unlock(client);
3148
3149 return 0;
3150}
3151
3152API int
3153nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3154{
3155 struct nc_ch_client *client;
3156
3157 if (!client_name) {
3158 ERRARG("client_name");
3159 return -1;
3160 } else if (!max_attempts) {
3161 ERRARG("max_attempts");
3162 return -1;
3163 }
3164
3165 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003166 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003167 if (!client) {
3168 return -1;
3169 }
3170
3171 client->max_attempts = max_attempts;
3172
3173 /* UNLOCK */
3174 nc_server_ch_client_unlock(client);
3175
3176 return 0;
3177}
3178
3179/* client lock is expected to be held */
3180static NC_MSG_TYPE
Michal Vaskoadf30f02019-06-24 09:34:47 +02003181nc_connect_ch_endpt(struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01003182{
Michal Vasko71090fc2016-05-24 16:37:28 +02003183 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003184 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003185 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003186 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003187
Michal Vasko4c612cd2021-02-05 08:53:42 +01003188 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 +01003189 if (sock < 0) {
Michal Vasko0b30e452021-03-03 10:30:15 +01003190 if (endpt->sock_pending > -1) {
3191 ++endpt->sock_retries;
3192 if (endpt->sock_retries == NC_SOCKET_CH_RETRIES) {
3193 ERR("Failed to connect socket %d after %d retries, closing.", endpt->sock_pending, NC_SOCKET_CH_RETRIES);
3194 close(endpt->sock_pending);
3195 endpt->sock_pending = -1;
3196 endpt->sock_retries = 0;
3197 }
Michal Vasko4c612cd2021-02-05 08:53:42 +01003198 }
Michal Vasko71090fc2016-05-24 16:37:28 +02003199 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003200 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00003201 /* no need to store the socket as pending any longer */
3202 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01003203
Michal Vasko131120a2018-05-29 15:44:02 +02003204 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003205 if (!(*session)) {
3206 ERRMEM;
3207 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003208 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02003209 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003210 }
3211 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01003212 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003213 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko77367452021-02-16 16:32:18 +01003214 lydict_insert_zc(server_opts.ctx, ip_host, &(*session)->host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003215 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003216
Michal Vaskob05053d2016-01-22 16:12:06 +01003217 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003218#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003219 if (endpt->ti == NC_TI_LIBSSH) {
3220 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003221 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003222 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003223
Michal Vasko71090fc2016-05-24 16:37:28 +02003224 if (ret < 0) {
3225 msgtype = NC_MSG_ERROR;
3226 goto fail;
3227 } else if (!ret) {
3228 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003229 goto fail;
3230 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003231 } else
3232#endif
Radek Krejci53691be2016-02-22 13:58:37 +01003233#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003234 if (endpt->ti == NC_TI_OPENSSL) {
3235 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003236 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003237 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003238
Michal Vasko71090fc2016-05-24 16:37:28 +02003239 if (ret < 0) {
3240 msgtype = NC_MSG_ERROR;
3241 goto fail;
3242 } else if (!ret) {
3243 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003244 goto fail;
3245 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003246 } else
3247#endif
3248 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003249 ERRINT;
3250 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003251 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003252 goto fail;
3253 }
3254
3255 /* assign new SID atomically */
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02003256 (*session)->id = ATOMIC_INC(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003257
3258 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003259 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003260 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003261 goto fail;
3262 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003263
3264 nc_gettimespec_mono(&ts_cur);
3265 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3266 nc_gettimespec_real(&ts_cur);
3267 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003268 (*session)->status = NC_STATUS_RUNNING;
3269
Michal Vasko71090fc2016-05-24 16:37:28 +02003270 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003271
3272fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003273 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003274 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003275 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003276}
3277
Michal Vasko2e6defd2016-10-07 15:48:15 +02003278struct nc_ch_client_thread_arg {
3279 char *client_name;
3280 void (*session_clb)(const char *client_name, struct nc_session *new_session);
3281};
3282
3283static struct nc_ch_client *
3284nc_server_ch_client_with_endpt_lock(const char *name)
3285{
3286 struct nc_ch_client *client;
3287
3288 while (1) {
3289 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003290 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003291 if (!client) {
3292 return NULL;
3293 }
3294 if (client->ch_endpt_count) {
3295 return client;
3296 }
3297 /* no endpoints defined yet */
3298
3299 /* UNLOCK */
3300 nc_server_ch_client_unlock(client);
3301
3302 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3303 }
3304
3305 return NULL;
3306}
3307
3308static int
3309nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3310{
Michal Vasko3f05a092018-03-13 10:39:49 +01003311 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003312 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003313 struct timespec ts;
3314 struct nc_ch_client *client;
3315
Michal Vasko2e6defd2016-10-07 15:48:15 +02003316 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003317 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003318
Michal Vasko0db3db52021-03-03 10:45:42 +01003319 session->flags |= NC_SESSION_CALLHOME;
3320
Michal Vasko2e6defd2016-10-07 15:48:15 +02003321 /* give the session to the user */
3322 data->session_clb(data->client_name, session);
3323
3324 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003325 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003326 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003327
Michal Vasko0db3db52021-03-03 10:45:42 +01003328 /* CH COND WAIT */
Michal Vaskoacf98472021-02-04 15:33:57 +01003329 r = pthread_cond_timedwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, &ts);
Michal Vasko3f05a092018-03-13 10:39:49 +01003330 if (!r) {
3331 /* we were woken up, something probably happened */
3332 if (session->status != NC_STATUS_RUNNING) {
3333 break;
3334 }
3335 } else if (r != ETIMEDOUT) {
3336 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3337 ret = -1;
3338 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003339 }
3340
Michal Vasko2e6defd2016-10-07 15:48:15 +02003341 /* check whether the client was not removed */
3342 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003343 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003344 if (!client) {
3345 /* client was removed, finish thread */
3346 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003347 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003348 ret = 1;
3349 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003350 }
3351
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003352 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003353 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003354 } else {
3355 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003356 }
3357
Michal Vasko9fb42272017-10-05 13:50:05 +02003358 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003359 if (!session->opts.server.ntf_status && idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003360 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3361 session->status = NC_STATUS_INVALID;
3362 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3363 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003364
3365 /* UNLOCK */
3366 nc_server_ch_client_unlock(client);
3367
3368 } while (session->status == NC_STATUS_RUNNING);
3369
Michal Vasko0db3db52021-03-03 10:45:42 +01003370 /* signal to nc_session_free() that CH registered this session not being valid anymore */
3371 session->flags &= ~NC_SESSION_CALLHOME;
3372
Michal Vasko27377422018-03-15 08:59:35 +01003373 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003374 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01003375
Michal Vasko3f05a092018-03-13 10:39:49 +01003376 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003377}
3378
3379static void *
3380nc_ch_client_thread(void *arg)
3381{
3382 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3383 NC_MSG_TYPE msgtype;
3384 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003385 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003386 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003387 struct nc_ch_endpt *cur_endpt;
3388 struct nc_session *session;
3389 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003390 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003391 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003392
3393 /* LOCK */
3394 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3395 if (!client) {
3396 goto cleanup;
3397 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003398 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003399
3400 cur_endpt = &client->ch_endpts[0];
3401 cur_endpt_name = strdup(cur_endpt->name);
3402
Michal Vasko29af44b2016-10-13 10:59:55 +02003403 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003404 while (1) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02003405 msgtype = nc_connect_ch_endpt(cur_endpt, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003406
3407 if (msgtype == NC_MSG_HELLO) {
3408 /* UNLOCK */
3409 nc_server_ch_client_unlock(client);
3410
Michal Vasko29af44b2016-10-13 10:59:55 +02003411 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003412 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3413 goto cleanup;
3414 }
Michal Vasko0a047f02021-03-03 10:30:52 +01003415 VRB("Call Home client \"%s\" session terminated.", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003416
3417 /* LOCK */
3418 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3419 if (!client) {
3420 goto cleanup;
3421 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003422 if (client->id != client_id) {
3423 nc_server_ch_client_unlock(client);
3424 goto cleanup;
3425 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003426
3427 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003428 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003429 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003430 /* UNLOCK */
3431 nc_server_ch_client_unlock(client);
3432
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003433 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
3434 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
Michal Vasko0a047f02021-03-03 10:30:52 +01003435 VRB("Call Home client \"%s\" reconnecting in %d seconds.", data->client_name, reconnect_in);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003436 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003437
3438 /* LOCK */
3439 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3440 if (!client) {
3441 goto cleanup;
3442 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003443 if (client->id != client_id) {
3444 nc_server_ch_client_unlock(client);
3445 goto cleanup;
3446 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003447 }
3448
3449 /* set next endpoint to try */
3450 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003451 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003452 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003453 /* we keep the current one but due to unlock/lock we have to find it again */
3454 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3455 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3456 break;
3457 }
3458 }
3459 if (next_endpt_index >= client->ch_endpt_count) {
3460 /* endpoint was removed, start with the first one */
3461 next_endpt_index = 0;
3462 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003463 } else {
3464 /* just get a random index */
3465 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003466 }
3467
Michal Vasko2e6defd2016-10-07 15:48:15 +02003468 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003469 /* UNLOCK */
3470 nc_server_ch_client_unlock(client);
3471
Michal Vasko2e6defd2016-10-07 15:48:15 +02003472 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003473 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3474
Michal Vasko6bb116b2016-10-26 13:53:46 +02003475 /* LOCK */
3476 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3477 if (!client) {
3478 goto cleanup;
3479 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003480 if (client->id != client_id) {
3481 nc_server_ch_client_unlock(client);
3482 goto cleanup;
3483 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003484
Michal Vasko2e6defd2016-10-07 15:48:15 +02003485 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003486
3487 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003488 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3489 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003490 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003491 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003492 }
3493
Peter Feiged05f2252018-09-03 08:09:47 +00003494 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003495 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003496 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003497 cur_attempts = 0;
3498 } else if (cur_attempts == client->max_attempts) {
3499 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003500 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003501 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003502 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003503 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003504 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003505 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003506 }
3507
3508 cur_attempts = 0;
3509 } /* else we keep the current one */
3510 }
Peter Feiged05f2252018-09-03 08:09:47 +00003511
3512 cur_endpt = &client->ch_endpts[next_endpt_index];
3513 free(cur_endpt_name);
3514 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003515 }
3516
3517cleanup:
3518 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003519 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003520 free(data->client_name);
3521 free(data);
3522 return NULL;
3523}
3524
3525API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02003526nc_connect_ch_client_dispatch(const char *client_name, void (*session_clb)(const char *client_name,
3527 struct nc_session *new_session))
Michal Vasko3f05a092018-03-13 10:39:49 +01003528{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003529 int ret;
3530 pthread_t tid;
3531 struct nc_ch_client_thread_arg *arg;
3532
3533 if (!client_name) {
3534 ERRARG("client_name");
3535 return -1;
3536 } else if (!session_clb) {
3537 ERRARG("session_clb");
3538 return -1;
3539 }
3540
3541 arg = malloc(sizeof *arg);
3542 if (!arg) {
3543 ERRMEM;
3544 return -1;
3545 }
3546 arg->client_name = strdup(client_name);
3547 if (!arg->client_name) {
3548 ERRMEM;
3549 free(arg);
3550 return -1;
3551 }
3552 arg->session_clb = session_clb;
3553
3554 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3555 if (ret) {
3556 ERR("Creating a new thread failed (%s).", strerror(ret));
3557 free(arg->client_name);
3558 free(arg);
3559 return -1;
3560 }
3561 /* the thread now manages arg */
3562
3563 pthread_detach(tid);
3564
3565 return 0;
3566}
3567
Radek Krejci53691be2016-02-22 13:58:37 +01003568#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003569
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003570API time_t
3571nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003572{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003573 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003574 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003575 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003576 }
3577
Michal Vasko2e6defd2016-10-07 15:48:15 +02003578 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003579}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003580
3581API void
Michal Vasko71dbd772021-03-23 14:08:37 +01003582nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01003583{
3584 if (!session || (session->side != NC_SERVER)) {
3585 ERRARG("session");
3586 return;
3587 }
3588
Michal Vasko71dbd772021-03-23 14:08:37 +01003589 ++session->opts.server.ntf_status;
3590}
3591
3592API void
3593nc_session_dec_notif_status(struct nc_session *session)
3594{
3595 if (!session || (session->side != NC_SERVER)) {
3596 ERRARG("session");
3597 return;
3598 }
3599
3600 if (session->opts.server.ntf_status) {
3601 --session->opts.server.ntf_status;
3602 }
Michal Vasko3486a7c2017-03-03 13:28:07 +01003603}
3604
3605API int
3606nc_session_get_notif_status(const struct nc_session *session)
3607{
3608 if (!session || (session->side != NC_SERVER)) {
3609 ERRARG("session");
3610 return 0;
3611 }
3612
3613 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003614}
Michal Vasko8f430592019-02-26 08:32:54 +01003615
3616API int
3617nc_session_is_callhome(const struct nc_session *session)
3618{
3619 if (!session || (session->side != NC_SERVER)) {
3620 ERRARG("session");
3621 return 0;
3622 }
3623
3624 if (session->flags & NC_SESSION_CALLHOME) {
3625 return 1;
3626 }
3627
3628 return 0;
3629}