blob: cb73b0d53e81d0f2a88284ffcb980ac3dd867a01 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_server.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libnetconf2 server session manipulation functions
Michal Vasko086311b2016-01-08 09:53:11 +01005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
7 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01008 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01009 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010012 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010013 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010014 */
apropp-molex4e903c32020-04-20 03:06:58 -040015#define _QNX_SOURCE /* getpeereid */
Olivier Matzac7fa2f2018-10-11 10:02:04 +020016#define _GNU_SOURCE /* signals, threads, SO_PEERCRED */
Michal Vasko086311b2016-01-08 09:53:11 +010017
Michal Vaskob83a3fa2021-05-26 09:53:42 +020018#include <arpa/inet.h>
Michal Vasko77e83572022-07-21 15:31:15 +020019#include <assert.h>
Michal Vasko086311b2016-01-08 09:53:11 +010020#include <errno.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020021#include <fcntl.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020022#include <netinet/in.h>
23#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020024#include <poll.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010025#include <pthread.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020026#include <pwd.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020027#include <signal.h>
28#include <stdint.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/socket.h>
32#include <sys/types.h>
33#include <sys/un.h>
34#include <time.h>
35#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010036
Michal Vasko7a20d2e2021-05-19 16:40:23 +020037#include "compat.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010038#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010039#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020040#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010041
Michal Vaskob48aa812016-01-18 14:13:09 +010042struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010043#ifdef NC_ENABLED_SSH
44 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
45#endif
46 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020047 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
48 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010049};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010050
fanchanghu966f2de2016-07-21 02:28:57 -040051static nc_rpc_clb global_rpc_clb = NULL;
52
Michal Vasko3031aae2016-01-27 16:07:18 +010053struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010054nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010055{
56 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010057 struct nc_endpt *endpt = NULL;
58
Michal Vaskoddce1212019-05-24 09:58:49 +020059 if (!name) {
60 ERRARG("endpt_name");
61 return NULL;
62 }
63
Michal Vaskoade892d2017-02-22 13:40:35 +010064 /* WRITE LOCK */
65 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010066
67 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020068 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010069 endpt = &server_opts.endpts[i];
70 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010071 }
72 }
73
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010074 if (!endpt) {
Michal Vasko05532772021-06-03 12:12:38 +020075 ERR(NULL, "Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010076 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020077 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010078 return NULL;
79 }
80
Michal Vaskoe2713da2016-08-22 16:06:40 +020081 if (idx) {
82 *idx = i;
83 }
84
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010085 return endpt;
86}
87
Michal Vaskoadf30f02019-06-24 09:34:47 +020088struct nc_ch_endpt *
89nc_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 +010090{
Michal Vaskoadf30f02019-06-24 09:34:47 +020091 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020092 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020093 struct nc_ch_endpt *endpt = NULL;
94
95 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +020096
Michal Vaskoddce1212019-05-24 09:58:49 +020097 if (!name) {
98 ERRARG("client_name");
99 return NULL;
100 }
101
Michal Vasko2e6defd2016-10-07 15:48:15 +0200102 /* READ LOCK */
103 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
104
105 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200106 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200107 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200108 if (!endpt_name && !ti) {
109 /* return only client */
110 break;
111 }
112 for (j = 0; j < client->ch_endpt_count; ++j) {
Michal Vasko530d95c2021-05-28 13:32:02 +0200113 if ((!endpt_name || !strcmp(client->ch_endpts[j].name, endpt_name)) &&
114 (!ti || (ti == client->ch_endpts[j].ti))) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200115 endpt = &client->ch_endpts[j];
116 break;
117 }
118 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200119 break;
120 }
121 }
122
123 if (!client) {
Michal Vasko05532772021-06-03 12:12:38 +0200124 ERR(NULL, "Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200125
Michal Vasko2e6defd2016-10-07 15:48:15 +0200126 /* READ UNLOCK */
127 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200128 } else if (endpt_name && ti && !endpt) {
Michal Vasko05532772021-06-03 12:12:38 +0200129 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200130
131 /* READ UNLOCK */
132 pthread_rwlock_unlock(&server_opts.ch_client_lock);
133 } else {
134 /* CH CLIENT LOCK */
135 pthread_mutex_lock(&client->lock);
136
137 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200138 }
139
Michal Vaskoadf30f02019-06-24 09:34:47 +0200140 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200141}
142
143void
144nc_server_ch_client_unlock(struct nc_ch_client *client)
145{
146 /* CH CLIENT UNLOCK */
147 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100148
149 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200150 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100151}
Michal Vasko086311b2016-01-08 09:53:11 +0100152
Michal Vasko1a38c862016-01-15 15:50:07 +0100153API void
154nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
155{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200156 if (!session) {
157 ERRARG("session");
158 return;
159 } else if (!reason) {
160 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100161 return;
162 }
163
Michal Vasko142cfea2017-08-07 10:12:11 +0200164 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
165 session->killed_by = 0;
166 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100167 session->term_reason = reason;
168}
169
Michal Vasko142cfea2017-08-07 10:12:11 +0200170API void
171nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
172{
173 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
174 ERRARG("session");
175 return;
176 } else if (!sid) {
177 ERRARG("sid");
178 return;
179 }
180
181 session->killed_by = sid;
182}
183
184API void
185nc_session_set_status(struct nc_session *session, NC_STATUS status)
186{
187 if (!session) {
188 ERRARG("session");
189 return;
190 } else if (!status) {
191 ERRARG("status");
192 return;
193 }
194
195 session->status = status;
196}
197
Michal Vasko086311b2016-01-08 09:53:11 +0100198int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200199nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100200{
Michal Vasko06c860d2018-07-09 16:08:52 +0200201 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100202 int is_ipv4, sock;
203 struct sockaddr_storage saddr;
204
205 struct sockaddr_in *saddr4;
206 struct sockaddr_in6 *saddr6;
207
Michal Vasko086311b2016-01-08 09:53:11 +0100208 if (!strchr(address, ':')) {
209 is_ipv4 = 1;
210 } else {
211 is_ipv4 = 0;
212 }
213
214 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
215 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200216 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100217 goto fail;
218 }
219
Michal Vaskobe52dc22018-10-17 09:28:17 +0200220 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200221 opt = 1;
222 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200223 ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko06c860d2018-07-09 16:08:52 +0200224 goto fail;
225 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100226 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200227 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vasko83ad17e2019-01-30 10:11:37 +0100228 goto fail;
229 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200230
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200231 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100232 goto fail;
233 }
234
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200235 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100236 if (is_ipv4) {
237 saddr4 = (struct sockaddr_in *)&saddr;
238
239 saddr4->sin_family = AF_INET;
240 saddr4->sin_port = htons(port);
241
242 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200243 ERR(NULL, "Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100244 goto fail;
245 }
246
247 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200248 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100249 goto fail;
250 }
251
252 } else {
253 saddr6 = (struct sockaddr_in6 *)&saddr;
254
255 saddr6->sin6_family = AF_INET6;
256 saddr6->sin6_port = htons(port);
257
258 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200259 ERR(NULL, "Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100260 goto fail;
261 }
262
263 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200264 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100265 goto fail;
266 }
267 }
268
Michal Vaskofb89d772016-01-08 12:25:35 +0100269 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200270 ERR(NULL, "Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100271 goto fail;
272 }
273
274 return sock;
275
276fail:
277 if (sock > -1) {
278 close(sock);
279 }
280
281 return -1;
282}
283
284int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200285nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
286{
287 struct sockaddr_un sun;
288 int sock = -1;
289
Michal Vasko93e96f12021-09-30 10:02:09 +0200290 if (strlen(address) > sizeof(sun.sun_path) - 1) {
291 ERR(NULL, "Socket path \"%s\" is longer than maximum length %d.", address, (int)(sizeof(sun.sun_path) - 1));
292 goto fail;
293 }
294
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200295 sock = socket(AF_UNIX, SOCK_STREAM, 0);
296 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200297 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200298 goto fail;
299 }
300
301 memset(&sun, 0, sizeof(sun));
302 sun.sun_family = AF_UNIX;
Michal Vasko93e96f12021-09-30 10:02:09 +0200303 snprintf(sun.sun_path, sizeof(sun.sun_path) - 1, "%s", address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200304
305 unlink(sun.sun_path);
306 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200307 ERR(NULL, "Could not bind \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200308 goto fail;
309 }
310
311 if (opts->mode != (mode_t)-1) {
312 if (chmod(sun.sun_path, opts->mode) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200313 ERR(NULL, "Failed to set unix socket permissions (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200314 goto fail;
315 }
316 }
317
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200318 if ((opts->uid != (uid_t)-1) || (opts->gid != (gid_t)-1)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200319 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200320 ERR(NULL, "Failed to set unix socket uid/gid (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200321 goto fail;
322 }
323 }
324
325 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200326 ERR(NULL, "Unable to start listening on \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200327 goto fail;
328 }
329
330 return sock;
331
332fail:
333 if (sock > -1) {
334 close(sock);
335 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200336 return -1;
337}
338
aPiecek90ff0242021-02-14 14:58:01 +0100339/**
340 * @brief Evaluate socket name for AF_UNIX socket.
341 * @param[in] acc_sock_fd is file descriptor for the accepted socket (a nonnegative).
342 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
343 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
344 * @return 0 if the stream socket is unnamed. Parameter host is set to NULL.
345 * @return -1 in case of error. Parameter host is set to NULL.
346 */
347static int
348sock_host_unix(int acc_sock_fd, char **host)
349{
350 char *sun_path;
351 struct sockaddr_storage saddr;
352 socklen_t addr_len;
353
354 *host = NULL;
355 saddr.ss_family = AF_UNIX;
356 addr_len = sizeof(saddr);
357
358 if (getsockname(acc_sock_fd, (struct sockaddr *)&saddr, &addr_len)) {
Michal Vasko05532772021-06-03 12:12:38 +0200359 ERR(NULL, "getsockname failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100360 return -1;
361 }
362
363 sun_path = ((struct sockaddr_un *)&saddr)->sun_path;
364 if (!sun_path) {
365 /* stream socket is unnamed */
366 return 0;
367 }
368
369 if (!(*host = strdup(sun_path))) {
370 ERRMEM;
371 return -1;
372 }
373
374 return 0;
375}
376
377/**
378 * @brief Evaluate socket name and port number for AF_INET socket.
379 * @param[in] addr is pointing to structure filled by accept function which was successful.
380 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
381 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
382 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
383 * @return -1 in case of error. Parameter host is set to NULL and port is unchanged.
384 */
385static int
386sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port)
387{
388 *host = malloc(INET_ADDRSTRLEN);
389 if (!(*host)) {
390 ERRMEM;
391 return -1;
392 }
393
aPiecek3da9b342021-02-18 15:00:03 +0100394 if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko69e98752022-12-14 14:20:17 +0100395 ERR(NULL, "inet_ntop failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100396 free(*host);
397 *host = NULL;
398 return -1;
399 }
400
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200401 *port = ntohs(addr->sin_port);
aPiecek90ff0242021-02-14 14:58:01 +0100402
403 return 0;
404}
405
406/**
407 * @brief Evaluate socket name and port number for AF_INET6 socket.
408 * @param[in] addr is pointing to structure filled by accept function which was successful.
409 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
410 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
411 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
412 * @return -1 in case of error. Parameter host is set to the NULL and port is unchanged.
413 */
414static int
415sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port)
416{
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200417 *host = malloc(INET6_ADDRSTRLEN);
aPiecek90ff0242021-02-14 14:58:01 +0100418 if (!(*host)) {
419 ERRMEM;
420 return -1;
421 }
422
aPiecek3da9b342021-02-18 15:00:03 +0100423 if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko69e98752022-12-14 14:20:17 +0100424 ERR(NULL, "inet_ntop failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100425 free(*host);
426 *host = NULL;
427 return -1;
428 }
429
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200430 *port = ntohs(addr->sin6_port);
aPiecek90ff0242021-02-14 14:58:01 +0100431
432 return 0;
433}
434
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200435int
Michal Vasko3031aae2016-01-27 16:07:18 +0100436nc_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 +0100437{
Michal Vaskof54cd352017-02-22 13:42:02 +0100438 sigset_t sigmask, origmask;
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200439 uint16_t i, j, pfd_count, client_port;
440 char *client_address;
Michal Vasko086311b2016-01-08 09:53:11 +0100441 struct pollfd *pfd;
442 struct sockaddr_storage saddr;
443 socklen_t saddr_len = sizeof(saddr);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200444 int ret, client_sock, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100445
446 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100447 if (!pfd) {
448 ERRMEM;
449 return -1;
450 }
451
Michal Vaskoac2f6182017-01-30 14:32:03 +0100452 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200453 if (binds[i].sock < 0) {
454 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200455 continue;
456 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200457 if (binds[i].pollin) {
458 binds[i].pollin = 0;
459 /* leftover pollin */
460 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100461 break;
462 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100463 pfd[pfd_count].fd = binds[i].sock;
464 pfd[pfd_count].events = POLLIN;
465 pfd[pfd_count].revents = 0;
466
467 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100468 }
469
Michal Vasko0a3f3752016-10-13 14:58:38 +0200470 if (sock == -1) {
471 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100472 sigfillset(&sigmask);
473 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100474 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100475 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
476
Michal Vasko0a3f3752016-10-13 14:58:38 +0200477 if (!ret) {
478 /* we timeouted */
479 free(pfd);
480 return 0;
481 } else if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200482 ERR(NULL, "Poll failed (%s).", strerror(errno));
Michal Vasko0a3f3752016-10-13 14:58:38 +0200483 free(pfd);
484 return -1;
485 }
Michal Vasko086311b2016-01-08 09:53:11 +0100486
Michal Vaskoac2f6182017-01-30 14:32:03 +0100487 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
488 /* adjust i so that indices in binds and pfd always match */
489 while (binds[i].sock != pfd[j].fd) {
490 ++i;
491 }
492
493 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200494 --ret;
495
496 if (!ret) {
497 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100498 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200499 break;
500 } else {
501 /* just remember the event for next time */
502 binds[i].pollin = 1;
503 }
504 }
Michal Vasko086311b2016-01-08 09:53:11 +0100505 }
506 }
507 free(pfd);
508
509 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100510 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100511 return -1;
512 }
513
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200514 /* accept connection */
515 client_sock = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
516 if (client_sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200517 ERR(NULL, "Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100518 return -1;
519 }
520
Michal Vasko0190bc32016-03-02 15:47:49 +0100521 /* make the socket non-blocking */
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200522 if (((flags = fcntl(client_sock, F_GETFL)) == -1) || (fcntl(client_sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200523 ERR(NULL, "Fcntl failed (%s).", strerror(errno));
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200524 goto fail;
Michal Vasko0190bc32016-03-02 15:47:49 +0100525 }
526
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200527 /* learn information about the client end */
528 if (saddr.ss_family == AF_UNIX) {
529 if (sock_host_unix(client_sock, &client_address)) {
530 goto fail;
531 }
532 client_port = 0;
533 } else if (saddr.ss_family == AF_INET) {
534 if (sock_host_inet((struct sockaddr_in *)&saddr, &client_address, &client_port)) {
535 goto fail;
536 }
537 } else if (saddr.ss_family == AF_INET6) {
538 if (sock_host_inet6((struct sockaddr_in6 *)&saddr, &client_address, &client_port)) {
539 goto fail;
540 }
541 } else {
542 ERR(NULL, "Source host of an unknown protocol family.");
543 goto fail;
aPiecek90ff0242021-02-14 14:58:01 +0100544 }
Michal Vasko086311b2016-01-08 09:53:11 +0100545
aPiecek90ff0242021-02-14 14:58:01 +0100546 if (saddr.ss_family == AF_UNIX) {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200547 VRB(NULL, "Accepted a connection on %s.", binds[i].address);
aPiecek90ff0242021-02-14 14:58:01 +0100548 } else {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200549 VRB(NULL, "Accepted a connection on %s:%u from %s:%u.", binds[i].address, binds[i].port, client_address, client_port);
Michal Vasko086311b2016-01-08 09:53:11 +0100550 }
551
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200552 if (host) {
553 *host = client_address;
554 } else {
555 free(client_address);
556 }
557 if (port) {
558 *port = client_port;
559 }
560 if (idx) {
561 *idx = i;
562 }
563 return client_sock;
564
565fail:
566 close(client_sock);
567 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100568}
569
Michal Vasko238b6c12021-12-14 15:14:09 +0100570API struct nc_server_reply *
Michal Vasko05532772021-06-03 12:12:38 +0200571nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100572{
Michal Vasko77367452021-02-16 16:32:18 +0100573 const char *identifier = NULL, *revision = NULL, *format = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100574 char *model_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100575 struct ly_out *out;
Michal Vasko9b1a9522021-03-15 16:24:26 +0100576 const struct lys_module *module = NULL, *mod;
Michal Vasko77367452021-02-16 16:32:18 +0100577 const struct lysp_submodule *submodule = NULL;
578 struct lyd_node *child, *err, *data = NULL;
579 LYS_OUTFORMAT outformat = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100580
Michal Vasko77367452021-02-16 16:32:18 +0100581 LY_LIST_FOR(lyd_child(rpc), child) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100582 if (!strcmp(child->schema->name, "identifier")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200583 identifier = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100584 } else if (!strcmp(child->schema->name, "version")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200585 revision = lyd_get_value(child);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200586 if (revision && (revision[0] == '\0')) {
Michal Vasko77367452021-02-16 16:32:18 +0100587 revision = NULL;
Radek Krejci1afa7792017-03-26 11:24:16 -0500588 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100589 } else if (!strcmp(child->schema->name, "format")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200590 format = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100591 }
592 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200593 VRB(session, "Module \"%s@%s\" was requested.", identifier, revision ? revision : "<any>");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100594
Michal Vasko77367452021-02-16 16:32:18 +0100595 /* check revision */
596 if (revision && (strlen(revision) != 10) && strcmp(revision, "1.0")) {
Michal Vasko93224072021-11-09 12:14:28 +0100597 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100598 nc_err_set_msg(err, "The requested version is not supported.", "en");
599 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100600 }
601
Michal Vasko77367452021-02-16 16:32:18 +0100602 if (revision) {
603 /* get specific module */
Michal Vasko93224072021-11-09 12:14:28 +0100604 module = ly_ctx_get_module(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100605 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100606 submodule = ly_ctx_get_submodule(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100607 }
608 } else {
609 /* try to get implemented, then latest module */
Michal Vasko93224072021-11-09 12:14:28 +0100610 module = ly_ctx_get_module_implemented(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100611 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100612 module = ly_ctx_get_module_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100613 }
614 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100615 submodule = ly_ctx_get_submodule_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100616 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200617 }
Michal Vasko77367452021-02-16 16:32:18 +0100618 if (!module && !submodule) {
Michal Vasko93224072021-11-09 12:14:28 +0100619 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko5ca5d972022-09-14 13:51:31 +0200620 nc_err_set_msg(err, "The requested module was not found.", "en");
Michal Vasko1a38c862016-01-15 15:50:07 +0100621 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100622 }
623
624 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100625 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko77367452021-02-16 16:32:18 +0100626 outformat = LYS_OUT_YANG;
Radek Krejci90fba642016-12-07 15:59:45 +0100627 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko77367452021-02-16 16:32:18 +0100628 outformat = LYS_OUT_YIN;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100629 } else {
Michal Vasko93224072021-11-09 12:14:28 +0100630 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100631 nc_err_set_msg(err, "The requested format is not supported.", "en");
632 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100633 }
Michal Vasko77367452021-02-16 16:32:18 +0100634
635 /* print */
636 ly_out_new_memory(&model_data, 0, &out);
637 if (module) {
638 lys_print_module(out, module, outformat, 0, 0);
639 } else {
640 lys_print_submodule(out, submodule, outformat, 0, 0);
641 }
642 ly_out_free(out, NULL, 0);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200643 if (!model_data) {
644 ERRINT;
645 return NULL;
646 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100647
Michal Vasko9b1a9522021-03-15 16:24:26 +0100648 /* create reply */
Michal Vasko93224072021-11-09 12:14:28 +0100649 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vasko9b1a9522021-03-15 16:24:26 +0100650 if (!mod || lyd_new_inner(NULL, mod, "get-schema", 0, &data)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100651 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200652 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100653 return NULL;
654 }
Michal Vasko9b1a9522021-03-15 16:24:26 +0100655 if (lyd_new_any(data, NULL, "data", model_data, 1, LYD_ANYDATA_STRING, 1, NULL)) {
656 ERRINT;
Michal Vaskoa50f68e2022-02-24 16:10:54 +0100657 free(model_data);
Michal Vasko9b1a9522021-03-15 16:24:26 +0100658 lyd_free_tree(data);
659 return NULL;
660 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100661
Radek Krejci36dfdb32016-09-01 16:56:35 +0200662 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100663}
664
Michal Vasko238b6c12021-12-14 15:14:09 +0100665API struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100666nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100667{
Michal Vasko428087d2016-01-14 16:04:28 +0100668 session->term_reason = NC_SESSION_TERM_CLOSED;
669 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100670}
671
Michal Vasko93224072021-11-09 12:14:28 +0100672/**
673 * @brief Initialize a context with default RPC callbacks if none are set.
674 *
675 * @param[in] ctx Context to initialize.
676 */
677static void
678nc_server_init_ctx(const struct ly_ctx *ctx)
Michal Vasko086311b2016-01-08 09:53:11 +0100679{
Michal Vasko77367452021-02-16 16:32:18 +0100680 struct lysc_node *rpc;
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100681
Michal Vasko238b6c12021-12-14 15:14:09 +0100682 if (global_rpc_clb) {
683 /* expect it to handle these RPCs as well */
684 return;
685 }
686
Michal Vasko05ba9df2016-01-13 14:40:27 +0100687 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100688 rpc = NULL;
689 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
690 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
691 }
Michal Vasko88639e92017-08-03 14:38:10 +0200692 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100693 rpc->priv = nc_clb_default_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100694 }
695
Michal Vasko93224072021-11-09 12:14:28 +0100696 /* set default <close-session> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100697 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200698 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100699 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100700 }
Michal Vasko93224072021-11-09 12:14:28 +0100701}
Michal Vasko05ba9df2016-01-13 14:40:27 +0100702
Michal Vasko93224072021-11-09 12:14:28 +0100703API int
704nc_server_init(void)
705{
706 pthread_rwlockattr_t attr, *attr_p = NULL;
707 int r;
708
709 nc_init();
Michal Vaskob48aa812016-01-18 14:13:09 +0100710
711 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500712 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100713
Michal Vasko93224072021-11-09 12:14:28 +0100714#ifdef HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
715 if ((r = pthread_rwlockattr_init(&attr))) {
716 ERR(NULL, "%s: failed init attribute (%s).", __func__, strerror(r));
717 goto error;
718 }
719 attr_p = &attr;
720 if ((r = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))) {
721 ERR(NULL, "%s: failed set attribute (%s).", __func__, strerror(r));
722 goto error;
723 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700724#endif
Michal Vasko93224072021-11-09 12:14:28 +0100725
726 if ((r = pthread_rwlock_init(&server_opts.endpt_lock, attr_p))) {
727 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
728 goto error;
729 }
730 if ((r = pthread_rwlock_init(&server_opts.ch_client_lock, attr_p))) {
731 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
732 goto error;
733 }
734
735 if (attr_p) {
736 pthread_rwlockattr_destroy(attr_p);
Frank Rimpler9f838b02018-07-25 06:44:03 +0000737 }
Michal Vasko086311b2016-01-08 09:53:11 +0100738 return 0;
Michal Vasko93224072021-11-09 12:14:28 +0100739
740error:
741 if (attr_p) {
742 pthread_rwlockattr_destroy(attr_p);
743 }
744 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100745}
746
Michal Vaskob48aa812016-01-18 14:13:09 +0100747API void
748nc_server_destroy(void)
749{
Michal Vasko1440a742021-03-31 11:11:03 +0200750 uint32_t i;
Radek Krejci658782b2016-12-04 22:04:55 +0100751
752 for (i = 0; i < server_opts.capabilities_count; i++) {
Michal Vasko93224072021-11-09 12:14:28 +0100753 free(server_opts.capabilities[i]);
Radek Krejci658782b2016-12-04 22:04:55 +0100754 }
755 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200756 server_opts.capabilities = NULL;
757 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200758 if (server_opts.content_id_data && server_opts.content_id_data_free) {
759 server_opts.content_id_data_free(server_opts.content_id_data);
760 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200761
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200762#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100763 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200764 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100765#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100766#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100767 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
768 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
769 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200770 server_opts.passwd_auth_data = NULL;
771 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100772
Michal Vasko17dfda92016-12-01 14:06:16 +0100773 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100774
775 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
776 server_opts.hostkey_data_free(server_opts.hostkey_data);
777 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200778 server_opts.hostkey_data = NULL;
779 server_opts.hostkey_data_free = NULL;
roman41a11e42022-06-22 09:27:08 +0200780
781 /* PAM */
782 free(server_opts.conf_name);
783 free(server_opts.conf_dir);
784 server_opts.conf_name = NULL;
785 server_opts.conf_dir = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100786#endif
787#ifdef NC_ENABLED_TLS
788 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
789 server_opts.server_cert_data_free(server_opts.server_cert_data);
790 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200791 server_opts.server_cert_data = NULL;
792 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100793 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
794 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
795 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200796 server_opts.trusted_cert_list_data = NULL;
797 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100798#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100799 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100800}
801
Michal Vasko086311b2016-01-08 09:53:11 +0100802API int
803nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
804{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200805 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
806 ERRARG("basic_mode");
807 return -1;
808 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
809 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100810 return -1;
811 }
812
813 server_opts.wd_basic_mode = basic_mode;
814 server_opts.wd_also_supported = also_supported;
815 return 0;
816}
817
Michal Vasko1a38c862016-01-15 15:50:07 +0100818API void
Michal Vasko55f03972016-04-13 08:56:01 +0200819nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
820{
821 if (!basic_mode && !also_supported) {
822 ERRARG("basic_mode and also_supported");
823 return;
824 }
825
826 if (basic_mode) {
827 *basic_mode = server_opts.wd_basic_mode;
828 }
829 if (also_supported) {
830 *also_supported = server_opts.wd_also_supported;
831 }
832}
833
Michal Vasko55f03972016-04-13 08:56:01 +0200834API int
Radek Krejci658782b2016-12-04 22:04:55 +0100835nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200836{
Michal Vasko93224072021-11-09 12:14:28 +0100837 void *mem;
Radek Krejci658782b2016-12-04 22:04:55 +0100838
839 if (!value || !value[0]) {
840 ERRARG("value must not be empty");
841 return EXIT_FAILURE;
842 }
843
Michal Vasko93224072021-11-09 12:14:28 +0100844 mem = realloc(server_opts.capabilities, (server_opts.capabilities_count + 1) * sizeof *server_opts.capabilities);
845 if (!mem) {
Radek Krejci658782b2016-12-04 22:04:55 +0100846 ERRMEM;
847 return EXIT_FAILURE;
848 }
Michal Vasko93224072021-11-09 12:14:28 +0100849 server_opts.capabilities = mem;
850
851 server_opts.capabilities[server_opts.capabilities_count] = strdup(value);
852 server_opts.capabilities_count++;
Radek Krejci658782b2016-12-04 22:04:55 +0100853
854 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200855}
856
Michal Vasko1a38c862016-01-15 15:50:07 +0100857API void
Michal Vasko1440a742021-03-31 11:11:03 +0200858nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
859 void (*free_user_data)(void *user_data))
860{
861 server_opts.content_id_clb = content_id_clb;
862 server_opts.content_id_data = user_data;
863 server_opts.content_id_data_free = free_user_data;
864}
865
866API void
Michal Vasko086311b2016-01-08 09:53:11 +0100867nc_server_set_hello_timeout(uint16_t hello_timeout)
868{
Michal Vasko086311b2016-01-08 09:53:11 +0100869 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100870}
871
Michal Vasko55f03972016-04-13 08:56:01 +0200872API uint16_t
873nc_server_get_hello_timeout(void)
874{
875 return server_opts.hello_timeout;
876}
877
Michal Vasko1a38c862016-01-15 15:50:07 +0100878API void
Michal Vasko086311b2016-01-08 09:53:11 +0100879nc_server_set_idle_timeout(uint16_t idle_timeout)
880{
Michal Vasko086311b2016-01-08 09:53:11 +0100881 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100882}
883
Michal Vasko55f03972016-04-13 08:56:01 +0200884API uint16_t
885nc_server_get_idle_timeout(void)
886{
887 return server_opts.idle_timeout;
888}
889
Michal Vasko71090fc2016-05-24 16:37:28 +0200890API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +0100891nc_accept_inout(int fdin, int fdout, const char *username, const struct ly_ctx *ctx, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100892{
Michal Vasko71090fc2016-05-24 16:37:28 +0200893 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200894 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200895
Michal Vasko93224072021-11-09 12:14:28 +0100896 if (!ctx) {
897 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +0200898 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200899 } else if (fdin < 0) {
900 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200901 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200902 } else if (fdout < 0) {
903 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200904 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200905 } else if (!username) {
906 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200907 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200908 } else if (!session) {
909 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200910 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100911 }
912
Michal Vasko93224072021-11-09 12:14:28 +0100913 /* init ctx as needed */
914 nc_server_init_ctx(ctx);
915
Michal Vasko086311b2016-01-08 09:53:11 +0100916 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200917 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100918 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100919 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200920 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100921 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100922 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100923
Michal Vasko086311b2016-01-08 09:53:11 +0100924 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100925 (*session)->ti_type = NC_TI_FD;
926 (*session)->ti.fd.in = fdin;
927 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100928
Michal Vasko93224072021-11-09 12:14:28 +0100929 /* assign context */
Michal Vasko1a38c862016-01-15 15:50:07 +0100930 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +0100931 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100932
Michal Vaskob48aa812016-01-18 14:13:09 +0100933 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200934 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100935
Michal Vasko086311b2016-01-08 09:53:11 +0100936 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200937 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200938 if (msgtype != NC_MSG_HELLO) {
939 nc_session_free(*session, NULL);
940 *session = NULL;
941 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100942 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200943
roman6ece9c52022-06-22 09:29:17 +0200944 nc_gettimespec_mono_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +0200945 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
roman6ece9c52022-06-22 09:29:17 +0200946 nc_gettimespec_real_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +0200947 (*session)->opts.server.session_start = ts_cur.tv_sec;
948
Michal Vasko1a38c862016-01-15 15:50:07 +0100949 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100950
Michal Vasko71090fc2016-05-24 16:37:28 +0200951 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100952}
Michal Vasko9e036d52016-01-08 10:49:26 +0100953
Michal Vaskob30b99c2016-07-26 11:35:43 +0200954static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100955nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
956{
957 uint8_t q_last;
958
959 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
960 ERRINT;
961 return;
962 }
963
964 /* get a unique queue value (by adding 1 to the last added value, if any) */
965 if (ps->queue_len) {
966 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
967 *id = ps->queue[q_last] + 1;
968 } else {
969 *id = 0;
970 }
971
972 /* add the id into the queue */
973 ++ps->queue_len;
974 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
975 ps->queue[q_last] = *id;
976}
977
978static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200979nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
980{
Michal Vasko74c345f2018-02-07 10:37:11 +0100981 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200982
983 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100984 /* get the actual queue idx */
985 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200986
987 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100988 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200989 /* another equal value, simply cannot be */
990 ERRINT;
991 }
Michal Vaskod8340032018-02-12 14:41:00 +0100992 if (found == 2) {
993 /* move the following values */
994 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
995 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100996 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200997 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100998 if (i == 0) {
999 found = 1;
1000 } else {
1001 /* this is not okay, our id is in the middle of the queue */
1002 found = 2;
1003 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001004 }
1005 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001006 if (!found) {
1007 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +01001008 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001009 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001010
Michal Vasko103fe632018-02-12 16:37:45 +01001011 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +01001012 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +01001013 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +01001014 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
1015 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001016}
1017
Michal Vaskof04a52a2016-04-07 10:52:10 +02001018int
Michal Vasko26043172016-07-26 14:08:59 +02001019nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001020{
1021 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001022 struct timespec ts;
1023
roman6ece9c52022-06-22 09:29:17 +02001024 nc_gettimespec_real_add(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001025
1026 /* LOCK */
1027 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1028 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001029 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001030 return -1;
1031 }
1032
Michal Vasko74c345f2018-02-07 10:37:11 +01001033 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +01001034 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko05532772021-06-03 12:12:38 +02001035 ERR(NULL, "%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +01001036 pthread_mutex_unlock(&ps->lock);
1037 return -1;
1038 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001039
1040 /* add ourselves into the queue */
1041 nc_ps_queue_add_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001042 DBL(NULL, "PS 0x%p TID %lu queue: added %u, head %u, length %u", ps, (long unsigned int)pthread_self(), *id,
Michal Vasko91290952019-09-27 11:30:55 +02001043 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001044
1045 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001046 while (ps->queue[ps->queue_begin] != *id) {
roman6ece9c52022-06-22 09:29:17 +02001047 nc_gettimespec_real_add(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001048
1049 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
1050 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301051 /**
1052 * This may happen when another thread releases the lock and broadcasts the condition
1053 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1054 * but when actually this thread was ready for condition.
1055 */
preetbhansali629dfc42018-12-17 16:04:40 +05301056 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301057 break;
1058 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001059
Michal Vasko05532772021-06-03 12:12:38 +02001060 ERR(NULL, "%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001061 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001062 nc_ps_queue_remove_id(ps, *id);
1063 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001064 return -1;
1065 }
1066 }
1067
Michal Vaskobe86fe32016-04-07 10:43:03 +02001068 /* UNLOCK */
1069 pthread_mutex_unlock(&ps->lock);
1070
1071 return 0;
1072}
1073
Michal Vaskof04a52a2016-04-07 10:52:10 +02001074int
Michal Vasko26043172016-07-26 14:08:59 +02001075nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001076{
1077 int ret;
1078 struct timespec ts;
1079
roman6ece9c52022-06-22 09:29:17 +02001080 nc_gettimespec_real_add(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001081
1082 /* LOCK */
1083 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1084 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001085 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001086 ret = -1;
1087 }
1088
Michal Vaskob30b99c2016-07-26 11:35:43 +02001089 /* we must be the first, it was our turn after all, right? */
1090 if (ps->queue[ps->queue_begin] != id) {
1091 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001092 /* UNLOCK */
1093 if (!ret) {
1094 pthread_mutex_unlock(&ps->lock);
1095 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001096 return -1;
1097 }
1098
Michal Vaskobe86fe32016-04-07 10:43:03 +02001099 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001100 nc_ps_queue_remove_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001101 DBL(NULL, "PS 0x%p TID %lu queue: removed %u, head %u, length %u", ps, (long unsigned int)pthread_self(), id,
Michal Vasko91290952019-09-27 11:30:55 +02001102 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001103
1104 /* broadcast to all other threads that the queue moved */
1105 pthread_cond_broadcast(&ps->cond);
1106
Michal Vaskobe86fe32016-04-07 10:43:03 +02001107 /* UNLOCK */
1108 if (!ret) {
1109 pthread_mutex_unlock(&ps->lock);
1110 }
1111
1112 return ret;
1113}
1114
Michal Vasko428087d2016-01-14 16:04:28 +01001115API struct nc_pollsession *
1116nc_ps_new(void)
1117{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001118 struct nc_pollsession *ps;
1119
1120 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001121 if (!ps) {
1122 ERRMEM;
1123 return NULL;
1124 }
Michal Vaskobe86fe32016-04-07 10:43:03 +02001125 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001126 pthread_mutex_init(&ps->lock, NULL);
1127
1128 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001129}
1130
1131API void
1132nc_ps_free(struct nc_pollsession *ps)
1133{
fanchanghu3d4e7212017-08-09 09:42:30 +08001134 uint16_t i;
1135
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001136 if (!ps) {
1137 return;
1138 }
1139
Michal Vaskobe86fe32016-04-07 10:43:03 +02001140 if (ps->queue_len) {
Michal Vasko05532772021-06-03 12:12:38 +02001141 ERR(NULL, "FATAL: Freeing a pollsession structure that is currently being worked with!");
Michal Vaskobe86fe32016-04-07 10:43:03 +02001142 }
1143
fanchanghu3d4e7212017-08-09 09:42:30 +08001144 for (i = 0; i < ps->session_count; i++) {
1145 free(ps->sessions[i]);
1146 }
1147
Michal Vasko428087d2016-01-14 16:04:28 +01001148 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001149 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001150 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001151
Michal Vasko428087d2016-01-14 16:04:28 +01001152 free(ps);
1153}
1154
1155API int
1156nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1157{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001158 uint8_t q_id;
1159
Michal Vasko45e53ae2016-04-07 11:46:03 +02001160 if (!ps) {
1161 ERRARG("ps");
1162 return -1;
1163 } else if (!session) {
1164 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001165 return -1;
1166 }
1167
Michal Vasko48a63ed2016-03-01 09:48:21 +01001168 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001169 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001170 return -1;
1171 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001172
Michal Vasko428087d2016-01-14 16:04:28 +01001173 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001174 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001175 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001176 ERRMEM;
1177 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001178 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001179 return -1;
1180 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001181 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1182 if (!ps->sessions[ps->session_count - 1]) {
1183 ERRMEM;
1184 --ps->session_count;
1185 /* UNLOCK */
1186 nc_ps_unlock(ps, q_id, __func__);
1187 return -1;
1188 }
1189 ps->sessions[ps->session_count - 1]->session = session;
1190 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001191
Michal Vasko48a63ed2016-03-01 09:48:21 +01001192 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001193 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001194}
1195
Michal Vasko48a63ed2016-03-01 09:48:21 +01001196static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001197_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001198{
1199 uint16_t i;
1200
Radek Krejcid5f978f2016-03-03 13:14:45 +01001201 if (index >= 0) {
1202 i = (uint16_t)index;
1203 goto remove;
1204 }
Michal Vasko428087d2016-01-14 16:04:28 +01001205 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001206 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001207remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001208 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001209 if (i <= ps->session_count) {
1210 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001211 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001212 }
1213 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001214 free(ps->sessions);
1215 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001216 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001217 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001218 return 0;
1219 }
1220 }
1221
Michal Vaskof0537d82016-01-29 14:42:38 +01001222 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001223}
1224
Michal Vasko48a63ed2016-03-01 09:48:21 +01001225API int
1226nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1227{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001228 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001229 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001230
Michal Vasko45e53ae2016-04-07 11:46:03 +02001231 if (!ps) {
1232 ERRARG("ps");
1233 return -1;
1234 } else if (!session) {
1235 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001236 return -1;
1237 }
1238
1239 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001240 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001241 return -1;
1242 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001243
Radek Krejcid5f978f2016-03-03 13:14:45 +01001244 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001245
1246 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001247 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001248
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001249 return ret || ret2 ? -1 : 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001250}
1251
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001252API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001253nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001254{
1255 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001256 struct nc_session *ret = NULL;
1257
1258 if (!ps) {
1259 ERRARG("ps");
1260 return NULL;
1261 }
1262
1263 /* LOCK */
1264 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1265 return NULL;
1266 }
1267
Michal Vasko4871c9d2017-10-09 14:48:39 +02001268 if (idx < ps->session_count) {
1269 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001270 }
1271
1272 /* UNLOCK */
1273 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1274
1275 return ret;
1276}
1277
Michal Vasko3ec3b112022-07-21 12:32:33 +02001278API struct nc_session *
1279nc_ps_find_session(const struct nc_pollsession *ps, nc_ps_session_match_cb match_cb, void *cb_data)
1280{
1281 uint8_t q_id;
1282 uint16_t i;
1283 struct nc_session *ret = NULL;
1284
1285 if (!ps) {
1286 ERRARG("ps");
1287 return NULL;
1288 }
1289
1290 /* LOCK */
1291 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1292 return NULL;
1293 }
1294
1295 for (i = 0; i < ps->session_count; ++i) {
1296 if (match_cb(ps->sessions[i]->session, cb_data)) {
1297 ret = ps->sessions[i]->session;
1298 break;
1299 }
1300 }
1301
1302 /* UNLOCK */
1303 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1304
1305 return ret;
1306}
1307
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001308API uint16_t
1309nc_ps_session_count(struct nc_pollsession *ps)
1310{
Michal Vasko47003942019-03-14 12:25:23 +01001311 uint8_t q_id;
1312 uint16_t session_count;
1313
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001314 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001315 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001316 return 0;
1317 }
1318
Michal Vasko47003942019-03-14 12:25:23 +01001319 /* LOCK (just for memory barrier so that we read the current value) */
1320 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1321 return 0;
1322 }
1323
1324 session_count = ps->session_count;
1325
1326 /* UNLOCK */
1327 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1328
1329 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001330}
1331
Michal Vasko77e83572022-07-21 15:31:15 +02001332static NC_MSG_TYPE
1333recv_rpc_check_msgid(struct nc_session *session, const struct lyd_node *envp)
1334{
1335 struct lyd_attr *attr;
1336
1337 assert(envp && !envp->schema);
1338
1339 /* find the message-id attribute */
1340 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1341 if (!strcmp(attr->name.name, "message-id")) {
1342 break;
1343 }
1344 }
1345
1346 if (!attr) {
1347 ERR(session, "Received an <rpc> without a message-id.");
1348 return NC_MSG_REPLY_ERR_MSGID;
1349 }
1350
1351 return NC_MSG_RPC;
1352}
1353
Michal Vasko131120a2018-05-29 15:44:02 +02001354/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001355 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001356 * NC_PSPOLL_TIMEOUT,
Michal Vasko77e83572022-07-21 15:31:15 +02001357 * NC_PSPOLL_BAD_RPC,
Michal Vasko71090fc2016-05-24 16:37:28 +02001358 * NC_PSPOLL_RPC
1359 */
1360static int
Michal Vasko131120a2018-05-29 15:44:02 +02001361nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001362{
Michal Vasko77367452021-02-16 16:32:18 +01001363 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001364 struct nc_server_reply *reply = NULL;
Michal Vasko939ffce2021-04-12 13:02:01 +02001365 struct lyd_node *e;
Michal Vasko77367452021-02-16 16:32:18 +01001366 int r, ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001367
Michal Vasko45e53ae2016-04-07 11:46:03 +02001368 if (!session) {
1369 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001370 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001371 } else if (!rpc) {
1372 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001373 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001374 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vasko05532772021-06-03 12:12:38 +02001375 ERR(session, "Invalid session to receive RPCs.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001376 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001377 }
1378
Michal Vasko93224072021-11-09 12:14:28 +01001379 *rpc = NULL;
1380
Michal Vasko77367452021-02-16 16:32:18 +01001381 /* get a message */
1382 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1383 if (r == -2) {
1384 /* malformed message */
Michal Vasko77e83572022-07-21 15:31:15 +02001385 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko93224072021-11-09 12:14:28 +01001386 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77e83572022-07-21 15:31:15 +02001387 goto cleanup;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001388 }
1389 if (r == -1) {
Michal Vasko77367452021-02-16 16:32:18 +01001390 return NC_PSPOLL_ERROR;
1391 } else if (!r) {
1392 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001393 }
1394
Michal Vasko77367452021-02-16 16:32:18 +01001395 *rpc = calloc(1, sizeof **rpc);
1396 if (!*rpc) {
1397 ERRMEM;
Michal Vasko77e83572022-07-21 15:31:15 +02001398 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko77367452021-02-16 16:32:18 +01001399 goto cleanup;
1400 }
1401
1402 /* parse the RPC */
Michal Vasko77e83572022-07-21 15:31:15 +02001403 if (!lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_RPC_NETCONF, &(*rpc)->envp, &(*rpc)->rpc)) {
1404 /* check message-id */
1405 if (recv_rpc_check_msgid(session, (*rpc)->envp) == NC_MSG_RPC) {
1406 /* valid RPC */
1407 ret = NC_PSPOLL_RPC;
1408 } else {
1409 /* no message-id */
1410 ret = NC_PSPOLL_BAD_RPC;
1411 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MISSING_ATTR, NC_ERR_TYPE_RPC, "message-id", "rpc"));
1412 }
1413 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001414 /* bad RPC received */
Michal Vasko77e83572022-07-21 15:31:15 +02001415 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko77367452021-02-16 16:32:18 +01001416
1417 if ((*rpc)->envp) {
1418 /* at least the envelopes were parsed */
Michal Vasko93224072021-11-09 12:14:28 +01001419 e = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
1420 nc_err_set_msg(e, ly_errmsg(session->ctx), "en");
Michal Vasko939ffce2021-04-12 13:02:01 +02001421 reply = nc_server_reply_err(e);
Michal Vasko77367452021-02-16 16:32:18 +01001422 } else if (session->version == NC_VERSION_11) {
Michal Vasko93224072021-11-09 12:14:28 +01001423 /* completely malformed message, NETCONF version 1.1 defines sending error reply from
1424 * the server (RFC 6241 sec. 3) */
1425 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77367452021-02-16 16:32:18 +01001426 }
Michal Vasko77367452021-02-16 16:32:18 +01001427 }
1428
1429cleanup:
Michal Vasko77e83572022-07-21 15:31:15 +02001430 if (reply) {
1431 /* send error reply */
1432 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, *rpc ? (*rpc)->envp : NULL, reply);
1433 nc_server_reply_free(reply);
1434 if (r != NC_MSG_REPLY) {
1435 ERR(session, "Failed to write reply (%s), terminating session.", nc_msgtype2str[r]);
1436 if (session->status != NC_STATUS_INVALID) {
1437 session->status = NC_STATUS_INVALID;
1438 session->term_reason = NC_SESSION_TERM_OTHER;
1439 }
1440 }
1441 }
1442
Michal Vasko77367452021-02-16 16:32:18 +01001443 ly_in_free(msg, 1);
1444 if (ret != NC_PSPOLL_RPC) {
1445 nc_server_rpc_free(*rpc);
1446 *rpc = NULL;
1447 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001448 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001449}
1450
fanchanghu966f2de2016-07-21 02:28:57 -04001451API void
1452nc_set_global_rpc_clb(nc_rpc_clb clb)
1453{
1454 global_rpc_clb = clb;
1455}
1456
Radek Krejci93e80222016-10-03 13:34:25 +02001457API NC_MSG_TYPE
1458nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1459{
Michal Vasko131120a2018-05-29 15:44:02 +02001460 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001461
1462 /* check parameters */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001463 if (!session || (session->side != NC_SERVER) || !nc_session_get_notif_status(session)) {
Radek Krejci93e80222016-10-03 13:34:25 +02001464 ERRARG("session");
1465 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001466 } else if (!notif || !notif->ntf || !notif->eventtime) {
Radek Krejci93e80222016-10-03 13:34:25 +02001467 ERRARG("notif");
1468 return NC_MSG_ERROR;
1469 }
1470
Michal Vasko131120a2018-05-29 15:44:02 +02001471 /* we do not need RPC lock for this, IO lock will be acquired properly */
1472 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001473 if (ret != NC_MSG_NOTIF) {
Michal Vasko05532772021-06-03 12:12:38 +02001474 ERR(session, "Failed to write notification (%s).", nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001475 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001476
Michal Vasko131120a2018-05-29 15:44:02 +02001477 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001478}
1479
Michal Vasko131120a2018-05-29 15:44:02 +02001480/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001481 * returns: NC_PSPOLL_ERROR,
1482 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1483 * NC_PSPOLL_REPLY_ERROR,
1484 * 0
1485 */
1486static int
Michal Vasko93224072021-11-09 12:14:28 +01001487nc_server_send_reply_io(struct nc_session *session, int io_timeout, const struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001488{
1489 nc_rpc_clb clb;
1490 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001491 const struct lysc_node *rpc_act = NULL;
1492 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001493 int ret = 0;
1494 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001495
Michal Vasko4a827e52016-03-03 10:59:00 +01001496 if (!rpc) {
1497 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001498 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001499 }
1500
Michal Vasko77367452021-02-16 16:32:18 +01001501 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001502 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001503 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001504 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001505 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001506 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001507 if (elem->schema->nodetype == LYS_ACTION) {
1508 rpc_act = elem->schema;
1509 break;
1510 }
Michal Vasko77367452021-02-16 16:32:18 +01001511 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001512 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001513 if (!rpc_act) {
1514 ERRINT;
1515 return NC_PSPOLL_ERROR;
1516 }
1517 }
1518
1519 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001520 if (!global_rpc_clb) {
1521 /* no callback, reply with a not-implemented error */
Michal Vasko93224072021-11-09 12:14:28 +01001522 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001523 } else {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001524 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001525 }
Michal Vasko428087d2016-01-14 16:04:28 +01001526 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001527 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001528 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001529 }
1530
1531 if (!reply) {
Michal Vasko93224072021-11-09 12:14:28 +01001532 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001533 }
Michal Vasko77367452021-02-16 16:32:18 +01001534 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001535 if (reply->type == NC_RPL_ERROR) {
1536 ret |= NC_PSPOLL_REPLY_ERROR;
1537 }
1538 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001539
Michal Vasko131120a2018-05-29 15:44:02 +02001540 if (r != NC_MSG_REPLY) {
Michal Vasko15469492021-06-09 08:40:48 +02001541 ERR(session, "Failed to write reply (%s).", nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001542 ret |= NC_PSPOLL_ERROR;
1543 }
Michal Vasko428087d2016-01-14 16:04:28 +01001544
1545 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1546 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1547 session->status = NC_STATUS_INVALID;
1548 }
1549
Michal Vasko71090fc2016-05-24 16:37:28 +02001550 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001551}
1552
Michal Vasko131120a2018-05-29 15:44:02 +02001553/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001554 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1555 * NC_PSPOLL_ERROR, (msg filled)
1556 * NC_PSPOLL_TIMEOUT,
1557 * NC_PSPOLL_RPC (some application data available),
1558 * NC_PSPOLL_SSH_CHANNEL,
1559 * NC_PSPOLL_SSH_MSG
1560 */
1561static int
Michal Vasko131120a2018-05-29 15:44:02 +02001562nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001563{
Michal Vasko9a327362017-01-11 11:31:46 +01001564 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001565 int r, ret = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001566
Michal Vasko9a327362017-01-11 11:31:46 +01001567#ifdef NC_ENABLED_SSH
1568 struct nc_session *new;
1569#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001570
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001571 /* check timeout first */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001572 if (!(session->flags & NC_SESSION_CALLHOME) && !nc_session_get_notif_status(session) && server_opts.idle_timeout &&
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001573 (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001574 sprintf(msg, "session idle timeout elapsed");
1575 session->status = NC_STATUS_INVALID;
1576 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1577 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1578 }
1579
Michal Vasko131120a2018-05-29 15:44:02 +02001580 r = nc_session_io_lock(session, io_timeout, __func__);
1581 if (r < 0) {
1582 sprintf(msg, "session IO lock failed to be acquired");
1583 return NC_PSPOLL_ERROR;
1584 } else if (!r) {
1585 return NC_PSPOLL_TIMEOUT;
1586 }
1587
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001588 switch (session->ti_type) {
1589#ifdef NC_ENABLED_SSH
1590 case NC_TI_LIBSSH:
1591 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001592 if (r == SSH_EOF) {
1593 sprintf(msg, "SSH channel unexpected EOF");
1594 session->status = NC_STATUS_INVALID;
1595 session->term_reason = NC_SESSION_TERM_DROPPED;
1596 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1597 } else if (r == SSH_ERROR) {
1598 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001599 session->status = NC_STATUS_INVALID;
1600 session->term_reason = NC_SESSION_TERM_OTHER;
1601 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001602 } else if (!r) {
1603 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1604 /* new SSH message */
1605 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1606 if (session->ti.libssh.next) {
1607 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001608 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
1609 (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko8dcaa882017-10-19 14:28:42 +02001610 /* new NETCONF SSH channel */
1611 ret = NC_PSPOLL_SSH_CHANNEL;
1612 break;
1613 }
1614 }
1615 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001616 break;
1617 }
1618 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001619
Michal Vasko8dcaa882017-10-19 14:28:42 +02001620 /* just some SSH message */
1621 ret = NC_PSPOLL_SSH_MSG;
1622 } else {
1623 ret = NC_PSPOLL_TIMEOUT;
1624 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001625 } else {
1626 /* we have some application data */
1627 ret = NC_PSPOLL_RPC;
1628 }
1629 break;
1630#endif
1631#ifdef NC_ENABLED_TLS
1632 case NC_TI_OPENSSL:
1633 r = SSL_pending(session->ti.tls);
1634 if (!r) {
1635 /* no data pending in the SSL buffer, poll fd */
1636 pfd.fd = SSL_get_rfd(session->ti.tls);
1637 if (pfd.fd < 0) {
1638 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1639 ret = NC_PSPOLL_ERROR;
1640 break;
1641 }
1642 pfd.events = POLLIN;
1643 pfd.revents = 0;
1644 r = poll(&pfd, 1, 0);
1645
1646 if ((r < 0) && (errno != EINTR)) {
1647 sprintf(msg, "poll failed (%s)", strerror(errno));
1648 session->status = NC_STATUS_INVALID;
1649 ret = NC_PSPOLL_ERROR;
1650 } else if (r > 0) {
1651 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1652 sprintf(msg, "communication socket unexpectedly closed");
1653 session->status = NC_STATUS_INVALID;
1654 session->term_reason = NC_SESSION_TERM_DROPPED;
1655 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1656 } else if (pfd.revents & POLLERR) {
1657 sprintf(msg, "communication socket error");
1658 session->status = NC_STATUS_INVALID;
1659 session->term_reason = NC_SESSION_TERM_OTHER;
1660 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1661 } else {
1662 ret = NC_PSPOLL_RPC;
1663 }
1664 } else {
1665 ret = NC_PSPOLL_TIMEOUT;
1666 }
1667 } else {
1668 ret = NC_PSPOLL_RPC;
1669 }
1670 break;
1671#endif
1672 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001673 case NC_TI_UNIX:
1674 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001675 pfd.events = POLLIN;
1676 pfd.revents = 0;
1677 r = poll(&pfd, 1, 0);
1678
1679 if ((r < 0) && (errno != EINTR)) {
1680 sprintf(msg, "poll failed (%s)", strerror(errno));
1681 session->status = NC_STATUS_INVALID;
1682 ret = NC_PSPOLL_ERROR;
1683 } else if (r > 0) {
1684 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1685 sprintf(msg, "communication socket unexpectedly closed");
1686 session->status = NC_STATUS_INVALID;
1687 session->term_reason = NC_SESSION_TERM_DROPPED;
1688 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1689 } else if (pfd.revents & POLLERR) {
1690 sprintf(msg, "communication socket error");
1691 session->status = NC_STATUS_INVALID;
1692 session->term_reason = NC_SESSION_TERM_OTHER;
1693 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1694 } else {
1695 ret = NC_PSPOLL_RPC;
1696 }
1697 } else {
1698 ret = NC_PSPOLL_TIMEOUT;
1699 }
1700 break;
1701 case NC_TI_NONE:
1702 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1703 ret = NC_PSPOLL_ERROR;
1704 break;
1705 }
1706
Michal Vasko131120a2018-05-29 15:44:02 +02001707 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001708 return ret;
1709}
1710
1711API int
1712nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1713{
Michal Vasko443faa02022-10-20 09:09:03 +02001714 int ret = NC_PSPOLL_ERROR, r;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001715 uint8_t q_id;
1716 uint16_t i, j;
1717 char msg[256];
1718 struct timespec ts_timeout, ts_cur;
1719 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001720 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001721 struct nc_server_rpc *rpc = NULL;
1722
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001723 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001724 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001725 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001726 }
1727
Michal Vaskoade892d2017-02-22 13:40:35 +01001728 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001729 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001730 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001731 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001732
Michal Vaskoade892d2017-02-22 13:40:35 +01001733 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001734 nc_ps_unlock(ps, q_id, __func__);
1735 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001736 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001737
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001738 /* fill timespecs */
roman6ece9c52022-06-22 09:29:17 +02001739 nc_gettimespec_mono_add(&ts_cur, 0);
Michal Vasko36c7be82017-02-22 13:37:59 +01001740 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001741 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001742 }
1743
1744 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001745 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001746 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001747 if (ps->last_event_session == ps->session_count - 1) {
1748 i = j = 0;
1749 } else {
1750 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001751 }
Michal Vasko9a327362017-01-11 11:31:46 +01001752 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001753 cur_ps_session = ps->sessions[i];
1754 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001755
Michal Vasko131120a2018-05-29 15:44:02 +02001756 /* SESSION RPC LOCK */
1757 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001758 if (r == -1) {
1759 ret = NC_PSPOLL_ERROR;
1760 } else if (r == 1) {
1761 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001762 switch (cur_ps_session->state) {
1763 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001764 if (cur_session->status == NC_STATUS_RUNNING) {
1765 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001766 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001767
Michal Vasko131120a2018-05-29 15:44:02 +02001768 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001769 switch (ret) {
1770 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001771 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001772 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001773 break;
1774 case NC_PSPOLL_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001775 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001776 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001777 break;
1778 case NC_PSPOLL_TIMEOUT:
1779#ifdef NC_ENABLED_SSH
1780 case NC_PSPOLL_SSH_CHANNEL:
1781 case NC_PSPOLL_SSH_MSG:
1782#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001783 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001784 break;
1785 case NC_PSPOLL_RPC:
1786 /* let's keep the state busy, we are not done with this session */
1787 break;
1788 }
1789 } else {
1790 /* session is not fine, let the caller know */
1791 ret = NC_PSPOLL_SESSION_TERM;
1792 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1793 ret |= NC_PSPOLL_SESSION_ERROR;
1794 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001795 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001796 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001797 break;
1798 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001799 /* it definitely should not be busy because we have the lock */
1800 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001801 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001802 break;
1803 case NC_PS_STATE_INVALID:
1804 /* we got it locked, but it will be freed, let it be */
1805 ret = NC_PSPOLL_TIMEOUT;
1806 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001807 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001808
Michal Vasko131120a2018-05-29 15:44:02 +02001809 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001810 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001811 /* SESSION RPC UNLOCK */
1812 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001813 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001814 } else {
1815 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001816 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001817 }
Michal Vasko428087d2016-01-14 16:04:28 +01001818
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001819 /* something happened */
1820 if (ret != NC_PSPOLL_TIMEOUT) {
1821 break;
1822 }
1823
Michal Vasko9a327362017-01-11 11:31:46 +01001824 if (i == ps->session_count - 1) {
1825 i = 0;
1826 } else {
1827 ++i;
1828 }
1829 } while (i != j);
1830
Michal Vaskoade892d2017-02-22 13:40:35 +01001831 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001832 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001833 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001834
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001835 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001836 /* final timeout */
1837 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001838 }
Michal Vasko428087d2016-01-14 16:04:28 +01001839 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001840 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001841
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001842 /* do we want to return the session? */
1843 switch (ret) {
1844 case NC_PSPOLL_RPC:
1845 case NC_PSPOLL_SESSION_TERM:
1846 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1847#ifdef NC_ENABLED_SSH
1848 case NC_PSPOLL_SSH_CHANNEL:
1849 case NC_PSPOLL_SSH_MSG:
1850#endif
1851 if (session) {
1852 *session = cur_session;
1853 }
1854 ps->last_event_session = i;
1855 break;
1856 default:
1857 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001858 }
Michal Vasko428087d2016-01-14 16:04:28 +01001859
Michal Vaskoade892d2017-02-22 13:40:35 +01001860 /* PS UNLOCK */
1861 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001862
Michal Vasko131120a2018-05-29 15:44:02 +02001863 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001864 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001865 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001866 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1867 if (cur_session->status != NC_STATUS_RUNNING) {
1868 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001869 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001870 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001871 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001872 }
1873 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001874 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001875
Michal Vasko7f1ee932018-10-11 09:41:42 +02001876 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001877 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001878 if (cur_session->status != NC_STATUS_RUNNING) {
1879 ret |= NC_PSPOLL_SESSION_TERM;
1880 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1881 ret |= NC_PSPOLL_SESSION_ERROR;
1882 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001883 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001884 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001885 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001886 }
Michal Vasko428087d2016-01-14 16:04:28 +01001887 }
Michal Vasko77367452021-02-16 16:32:18 +01001888 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001889
Michal Vasko131120a2018-05-29 15:44:02 +02001890 /* SESSION RPC UNLOCK */
1891 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001892 }
1893
Michal Vasko48a63ed2016-03-01 09:48:21 +01001894 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001895}
1896
Michal Vaskod09eae62016-02-01 10:32:52 +01001897API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001898nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001899{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001900 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001901 uint16_t i;
1902 struct nc_session *session;
1903
Michal Vasko9a25e932016-02-01 10:36:42 +01001904 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001905 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001906 return;
1907 }
1908
Michal Vasko48a63ed2016-03-01 09:48:21 +01001909 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001910 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001911 return;
1912 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001913
Michal Vasko48a63ed2016-03-01 09:48:21 +01001914 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001915 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001916 nc_session_free(ps->sessions[i]->session, data_free);
1917 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001918 }
1919 free(ps->sessions);
1920 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001921 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001922 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001923 } else {
1924 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001925 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1926 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001927 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001928 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001929 continue;
1930 }
1931
1932 ++i;
1933 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001934 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001935
1936 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001937 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001938}
1939
Michal Vasko5f352c52019-07-10 16:12:06 +02001940static int
apropp-molex4e903c32020-04-20 03:06:58 -04001941nc_get_uid(int sock, uid_t *uid)
1942{
Michal Vaskod3910912020-04-20 09:12:49 +02001943 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04001944
Michal Vaskod3910912020-04-20 09:12:49 +02001945#ifdef SO_PEERCRED
1946 struct ucred ucred;
1947 socklen_t len;
1948 len = sizeof(ucred);
1949 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
1950 if (!ret) {
1951 *uid = ucred.uid;
1952 }
1953#else
1954 ret = getpeereid(sock, uid, NULL);
1955#endif
1956
1957 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001958 ERR(NULL, "Failed to get credentials from unix socket (%s).", strerror(errno));
Michal Vaskod3910912020-04-20 09:12:49 +02001959 return -1;
1960 }
apropp-molex4e903c32020-04-20 03:06:58 -04001961 return 0;
1962}
1963
1964static int
Michal Vasko5f352c52019-07-10 16:12:06 +02001965nc_accept_unix(struct nc_session *session, int sock)
1966{
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001967#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001968 struct passwd *pw, pw_buf;
Michal Vasko5f352c52019-07-10 16:12:06 +02001969 char *username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001970 session->ti_type = NC_TI_UNIX;
Michal Vasko143aa142021-10-01 15:31:48 +02001971 uid_t uid = 0;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001972 char *buf = NULL;
1973 size_t buf_len = 0;
Michal Vasko5f352c52019-07-10 16:12:06 +02001974
Michal Vaskod3910912020-04-20 09:12:49 +02001975 if (nc_get_uid(sock, &uid)) {
1976 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02001977 return -1;
1978 }
1979
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001980 pw = nc_getpwuid(uid, &pw_buf, &buf, &buf_len);
Michal Vasko5f352c52019-07-10 16:12:06 +02001981 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001982 ERR(NULL, "Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02001983 close(sock);
1984 return -1;
1985 }
1986
1987 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001988 free(buf);
Michal Vasko5f352c52019-07-10 16:12:06 +02001989 if (username == NULL) {
1990 ERRMEM;
1991 close(sock);
1992 return -1;
1993 }
Michal Vasko93224072021-11-09 12:14:28 +01001994 session->username = username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001995
1996 session->ti.unixsock.sock = sock;
1997
1998 return 1;
Claus Klein22091912020-01-20 13:45:47 +01001999#else
2000 return -1;
2001#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02002002}
2003
Michal Vaskoe2713da2016-08-22 16:06:40 +02002004API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002005nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01002006{
Michal Vasko3031aae2016-01-27 16:07:18 +01002007 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01002008 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01002009
Michal Vasko45e53ae2016-04-07 11:46:03 +02002010 if (!name) {
2011 ERRARG("name");
2012 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01002013 }
2014
Michal Vaskoade892d2017-02-22 13:40:35 +01002015 /* BIND LOCK */
2016 pthread_mutex_lock(&server_opts.bind_lock);
2017
2018 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002019 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002020
2021 /* check name uniqueness */
2022 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002023 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002024 ERR(NULL, "Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01002025 ret = -1;
2026 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01002027 }
2028 }
2029
Michal Vasko93224072021-11-09 12:14:28 +01002030 server_opts.endpts = nc_realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002031 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01002032 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002033 ret = -1;
2034 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002035 }
Michal Vasko93224072021-11-09 12:14:28 +01002036 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
2037 ++server_opts.endpt_count;
2038
2039 server_opts.endpts[server_opts.endpt_count - 1].name = strdup(name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002040 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002041 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
2042 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
2043 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002044
Michal Vaskoe2713da2016-08-22 16:06:40 +02002045 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002046 if (!server_opts.binds) {
2047 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002048 ret = -1;
2049 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01002050 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002051
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002052 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002053 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
2054
2055 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002056#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002057 case NC_TI_LIBSSH:
2058 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2059 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
2060 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002061 ret = -1;
2062 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002063 }
2064 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
roman41a11e42022-06-22 09:27:08 +02002065 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002066 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002067 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002068 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002069#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02002070#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002071 case NC_TI_OPENSSL:
2072 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2073 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
2074 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002075 ret = -1;
2076 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002077 }
2078 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002079#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002080 case NC_TI_UNIX:
2081 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
2082 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
2083 ERRMEM;
2084 ret = -1;
2085 goto cleanup;
2086 }
2087 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
2088 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
2089 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
2090 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002091 default:
2092 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01002093 ret = -1;
2094 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002095 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002096
Michal Vaskoade892d2017-02-22 13:40:35 +01002097cleanup:
2098 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002099 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002100
Michal Vaskoade892d2017-02-22 13:40:35 +01002101 /* BIND UNLOCK */
2102 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002103
Michal Vaskoade892d2017-02-22 13:40:35 +01002104 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002105}
2106
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002107API int
Michal Vasko59050372016-11-22 14:33:55 +01002108nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01002109{
2110 uint32_t i;
2111 int ret = -1;
2112
Michal Vaskoade892d2017-02-22 13:40:35 +01002113 /* BIND LOCK */
2114 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002115
Michal Vaskoade892d2017-02-22 13:40:35 +01002116 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002117 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002118
Michal Vasko59050372016-11-22 14:33:55 +01002119 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002120 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01002121 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002122 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002123 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002124#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002125 case NC_TI_LIBSSH:
2126 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2127 free(server_opts.endpts[i].opts.ssh);
2128 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002129#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002130#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002131 case NC_TI_OPENSSL:
2132 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2133 free(server_opts.endpts[i].opts.tls);
2134 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002135#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002136 case NC_TI_UNIX:
2137 free(server_opts.endpts[i].opts.unixsock);
2138 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002139 default:
2140 ERRINT;
2141 /* won't get here ...*/
2142 break;
2143 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002144 ret = 0;
2145 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002146 free(server_opts.endpts);
2147 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002148
2149 /* remove all binds */
2150 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002151 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002152 if (server_opts.binds[i].sock > -1) {
2153 close(server_opts.binds[i].sock);
2154 }
2155 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002156 free(server_opts.binds);
2157 server_opts.binds = NULL;
2158
Michal Vasko3031aae2016-01-27 16:07:18 +01002159 server_opts.endpt_count = 0;
2160
Michal Vasko1a38c862016-01-15 15:50:07 +01002161 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002162 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002163 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002164 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002165 /* remove endpt */
Michal Vasko93224072021-11-09 12:14:28 +01002166 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002167 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002168#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002169 case NC_TI_LIBSSH:
2170 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2171 free(server_opts.endpts[i].opts.ssh);
2172 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002173#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002174#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002175 case NC_TI_OPENSSL:
2176 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2177 free(server_opts.endpts[i].opts.tls);
2178 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002179#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002180 case NC_TI_UNIX:
2181 free(server_opts.endpts[i].opts.unixsock);
2182 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002183 default:
2184 ERRINT;
2185 break;
2186 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002187
Michal Vaskoe2713da2016-08-22 16:06:40 +02002188 /* remove bind(s) */
Michal Vasko93224072021-11-09 12:14:28 +01002189 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002190 if (server_opts.binds[i].sock > -1) {
2191 close(server_opts.binds[i].sock);
2192 }
2193
2194 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002195 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002196 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002197 free(server_opts.binds);
2198 server_opts.binds = NULL;
2199 free(server_opts.endpts);
2200 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002201 } else if (i < server_opts.endpt_count) {
2202 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2203 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002204 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002205
2206 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002207 if (name) {
2208 break;
2209 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002210 }
2211 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002212 }
2213
Michal Vaskoade892d2017-02-22 13:40:35 +01002214 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002215 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002216
Michal Vaskoade892d2017-02-22 13:40:35 +01002217 /* BIND UNLOCK */
2218 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002219
2220 return ret;
2221}
2222
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002223API int
2224nc_server_endpt_count(void)
2225{
2226 return server_opts.endpt_count;
2227}
2228
Michal Vasko1b5973e2020-01-30 16:05:46 +01002229API int
2230nc_server_is_endpt(const char *name)
2231{
2232 uint16_t i;
2233 int found = 0;
2234
Michal Vaskofb1724b2020-01-31 11:02:00 +01002235 if (!name) {
2236 return found;
2237 }
2238
Michal Vasko1b5973e2020-01-30 16:05:46 +01002239 /* ENDPT READ LOCK */
2240 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2241
2242 /* check name uniqueness */
2243 for (i = 0; i < server_opts.endpt_count; ++i) {
2244 if (!strcmp(server_opts.endpts[i].name, name)) {
2245 found = 1;
2246 break;
2247 }
2248 }
2249
2250 /* ENDPT UNLOCK */
2251 pthread_rwlock_unlock(&server_opts.endpt_lock);
2252
2253 return found;
2254}
2255
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002256int
2257nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2258{
2259 struct nc_endpt *endpt;
2260 struct nc_bind *bind = NULL;
2261 uint16_t i;
2262 int sock = -1, set_addr, ret = 0;
2263
2264 if (!endpt_name) {
2265 ERRARG("endpt_name");
2266 return -1;
2267 } else if ((!address && !port) || (address && port)) {
2268 ERRARG("address and port");
2269 return -1;
2270 }
2271
2272 if (address) {
2273 set_addr = 1;
2274 } else {
2275 set_addr = 0;
2276 }
2277
2278 /* BIND LOCK */
2279 pthread_mutex_lock(&server_opts.bind_lock);
2280
2281 /* ENDPT LOCK */
2282 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2283 if (!endpt) {
2284 /* BIND UNLOCK */
2285 pthread_mutex_unlock(&server_opts.bind_lock);
2286 return -1;
2287 }
2288
2289 bind = &server_opts.binds[i];
2290
2291 if (set_addr) {
2292 port = bind->port;
2293 } else {
2294 address = bind->address;
2295 }
2296
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002297 if (!set_addr && (endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002298 ret = -1;
2299 goto cleanup;
2300 }
2301
2302 /* we have all the information we need to create a listening socket */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002303 if (address && (port || (endpt->ti == NC_TI_UNIX))) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002304 /* create new socket, close the old one */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002305 if (endpt->ti == NC_TI_UNIX) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002306 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002307 } else {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002308 sock = nc_sock_listen_inet(address, port, &endpt->ka);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002309 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002310 if (sock == -1) {
2311 ret = -1;
2312 goto cleanup;
2313 }
2314
2315 if (bind->sock > -1) {
2316 close(bind->sock);
2317 }
2318 bind->sock = sock;
2319 } /* else we are just setting address or port */
2320
2321 if (set_addr) {
Michal Vasko93224072021-11-09 12:14:28 +01002322 free(bind->address);
2323 bind->address = strdup(address);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002324 } else {
2325 bind->port = port;
2326 }
2327
2328 if (sock > -1) {
Michal Vasko946cacb2020-08-12 11:18:08 +02002329 switch (endpt->ti) {
2330 case NC_TI_UNIX:
Michal Vasko05532772021-06-03 12:12:38 +02002331 VRB(NULL, "Listening on %s for UNIX connections.", address);
Michal Vasko946cacb2020-08-12 11:18:08 +02002332 break;
2333#ifdef NC_ENABLED_SSH
2334 case NC_TI_LIBSSH:
Michal Vasko05532772021-06-03 12:12:38 +02002335 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002336 break;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002337#endif
Michal Vasko946cacb2020-08-12 11:18:08 +02002338#ifdef NC_ENABLED_TLS
2339 case NC_TI_OPENSSL:
Michal Vasko05532772021-06-03 12:12:38 +02002340 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002341 break;
2342#endif
2343 default:
2344 ERRINT;
2345 break;
2346 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002347 }
2348
2349cleanup:
2350 /* ENDPT UNLOCK */
2351 pthread_rwlock_unlock(&server_opts.endpt_lock);
2352
2353 /* BIND UNLOCK */
2354 pthread_mutex_unlock(&server_opts.bind_lock);
2355
2356 return ret;
2357}
2358
2359API int
2360nc_server_endpt_set_address(const char *endpt_name, const char *address)
2361{
2362 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2363}
2364
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002365#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002366
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002367API int
2368nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2369{
2370 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2371}
2372
Michal Vasko946cacb2020-08-12 11:18:08 +02002373#endif
2374
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002375API int
2376nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2377{
2378 struct nc_endpt *endpt;
2379 uint16_t i;
2380 int ret = 0;
2381
2382 if (!endpt_name) {
2383 ERRARG("endpt_name");
2384 return -1;
2385 } else if (mode == 0) {
2386 ERRARG("mode");
2387 return -1;
2388 }
2389
2390 /* ENDPT LOCK */
2391 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002392 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002393 return -1;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002394 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002395
2396 if (endpt->ti != NC_TI_UNIX) {
2397 ret = -1;
2398 goto cleanup;
2399 }
2400
2401 endpt->opts.unixsock->mode = mode;
2402 endpt->opts.unixsock->uid = uid;
2403 endpt->opts.unixsock->gid = gid;
2404
2405cleanup:
2406 /* ENDPT UNLOCK */
2407 pthread_rwlock_unlock(&server_opts.endpt_lock);
2408
2409 return ret;
2410}
2411
2412API int
2413nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2414{
2415 struct nc_endpt *endpt;
2416 int ret = 0;
2417
2418 if (!endpt_name) {
2419 ERRARG("endpt_name");
2420 return -1;
2421 }
2422
2423 /* ENDPT LOCK */
2424 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2425 if (!endpt) {
2426 return -1;
2427 }
2428
2429 endpt->ka.enabled = (enable ? 1 : 0);
2430
2431 /* ENDPT UNLOCK */
2432 pthread_rwlock_unlock(&server_opts.endpt_lock);
2433
2434 return ret;
2435}
2436
2437API int
2438nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2439{
2440 struct nc_endpt *endpt;
2441 int ret = 0;
2442
2443 if (!endpt_name) {
2444 ERRARG("endpt_name");
2445 return -1;
2446 }
2447
2448 /* ENDPT LOCK */
2449 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2450 if (!endpt) {
2451 return -1;
2452 }
2453
2454 if (idle_time > -1) {
2455 endpt->ka.idle_time = idle_time;
2456 }
2457 if (max_probes > -1) {
2458 endpt->ka.max_probes = max_probes;
2459 }
2460 if (probe_interval > -1) {
2461 endpt->ka.probe_interval = probe_interval;
2462 }
2463
2464 /* ENDPT UNLOCK */
2465 pthread_rwlock_unlock(&server_opts.endpt_lock);
2466
2467 return ret;
2468}
2469
Michal Vasko71090fc2016-05-24 16:37:28 +02002470API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +01002471nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002472{
Michal Vasko71090fc2016-05-24 16:37:28 +02002473 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002474 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002475 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002476 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002477 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002478
Michal Vasko93224072021-11-09 12:14:28 +01002479 if (!ctx) {
2480 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +02002481 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002482 } else if (!session) {
2483 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002484 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002485 }
2486
Michal Vasko93224072021-11-09 12:14:28 +01002487 /* init ctx as needed */
2488 nc_server_init_ctx(ctx);
2489
Michal Vaskoade892d2017-02-22 13:40:35 +01002490 /* BIND LOCK */
2491 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002492
2493 if (!server_opts.endpt_count) {
Michal Vasko05532772021-06-03 12:12:38 +02002494 ERR(NULL, "No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002495 /* BIND UNLOCK */
2496 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002497 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002498 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002499
Michal Vaskoe2713da2016-08-22 16:06:40 +02002500 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002501 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002502 /* BIND UNLOCK */
2503 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002504 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002505 if (!ret) {
2506 return NC_MSG_WOULDBLOCK;
2507 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002508 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002509 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002510
2511 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2512 /* ENDPT READ LOCK */
2513 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2514
2515 /* BIND UNLOCK */
2516 pthread_mutex_unlock(&server_opts.bind_lock);
2517
Michal Vaskob48aa812016-01-18 14:13:09 +01002518 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002519
Michal Vasko131120a2018-05-29 15:44:02 +02002520 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002521 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002522 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002523 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002524 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002525 msgtype = NC_MSG_ERROR;
2526 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002527 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002528 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01002529 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko1a38c862016-01-15 15:50:07 +01002530 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01002531 (*session)->host = host;
Michal Vasko1a38c862016-01-15 15:50:07 +01002532 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002533
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002534 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002535#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002536 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2537 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002538 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002539 if (ret < 0) {
2540 msgtype = NC_MSG_ERROR;
2541 goto cleanup;
2542 } else if (!ret) {
2543 msgtype = NC_MSG_WOULDBLOCK;
2544 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002545 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002546 } else
2547#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002548#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002549 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2550 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002551 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002552 if (ret < 0) {
2553 msgtype = NC_MSG_ERROR;
2554 goto cleanup;
2555 } else if (!ret) {
2556 msgtype = NC_MSG_WOULDBLOCK;
2557 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002558 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002559 } else
2560#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002561 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2562 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2563 ret = nc_accept_unix(*session, sock);
2564 if (ret < 0) {
2565 msgtype = NC_MSG_ERROR;
2566 goto cleanup;
2567 }
2568 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002569 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002570 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002571 msgtype = NC_MSG_ERROR;
2572 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002573 }
2574
Michal Vasko2cc4c682016-03-01 09:16:48 +01002575 (*session)->data = NULL;
2576
Michal Vaskoade892d2017-02-22 13:40:35 +01002577 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002578 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002579
Michal Vaskob48aa812016-01-18 14:13:09 +01002580 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002581 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002582
Michal Vasko9e036d52016-01-08 10:49:26 +01002583 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002584 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002585 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002586 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002587 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002588 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002589 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002590
roman6ece9c52022-06-22 09:29:17 +02002591 nc_gettimespec_mono_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02002592 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
roman6ece9c52022-06-22 09:29:17 +02002593 nc_gettimespec_real_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02002594 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002595 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002596
Michal Vasko71090fc2016-05-24 16:37:28 +02002597 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002598
Michal Vasko71090fc2016-05-24 16:37:28 +02002599cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002600 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002601 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002602
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002603 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002604 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002605 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002606}
2607
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002608#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002609
Michal Vaskoadf30f02019-06-24 09:34:47 +02002610/* client is expected to be locked */
2611static int
2612_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 +02002613{
2614 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002615 int ret = -1;
2616
2617 if (!endpt_name) {
2618 /* remove all endpoints */
2619 for (i = 0; i < client->ch_endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002620 free(client->ch_endpts[i].name);
2621 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002622 if (client->ch_endpts[i].sock_pending != -1) {
2623 close(client->ch_endpts[i].sock_pending);
2624 }
2625 switch (client->ch_endpts[i].ti) {
2626#ifdef NC_ENABLED_SSH
2627 case NC_TI_LIBSSH:
2628 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2629 free(client->ch_endpts[i].opts.ssh);
2630 break;
2631#endif
2632#ifdef NC_ENABLED_TLS
2633 case NC_TI_OPENSSL:
2634 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2635 free(client->ch_endpts[i].opts.tls);
2636 break;
2637#endif
2638 default:
2639 ERRINT;
2640 /* won't get here ...*/
2641 break;
2642 }
2643 }
2644 free(client->ch_endpts);
2645 client->ch_endpts = NULL;
2646 client->ch_endpt_count = 0;
2647
2648 ret = 0;
2649 } else {
2650 for (i = 0; i < client->ch_endpt_count; ++i) {
2651 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
Michal Vasko93224072021-11-09 12:14:28 +01002652 free(client->ch_endpts[i].name);
2653 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002654 if (client->ch_endpts[i].sock_pending != -1) {
2655 close(client->ch_endpts[i].sock_pending);
2656 }
2657 switch (client->ch_endpts[i].ti) {
2658#ifdef NC_ENABLED_SSH
2659 case NC_TI_LIBSSH:
2660 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2661 free(client->ch_endpts[i].opts.ssh);
2662 break;
2663#endif
2664#ifdef NC_ENABLED_TLS
2665 case NC_TI_OPENSSL:
2666 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2667 free(client->ch_endpts[i].opts.tls);
2668 break;
2669#endif
2670 default:
2671 ERRINT;
2672 /* won't get here ...*/
2673 break;
2674 }
2675
2676 /* move last endpoint to the empty space */
2677 --client->ch_endpt_count;
2678 if (i < client->ch_endpt_count) {
2679 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2680 } else if (!server_opts.ch_client_count) {
2681 free(server_opts.ch_clients);
2682 server_opts.ch_clients = NULL;
2683 }
2684
2685 ret = 0;
2686 break;
2687 }
2688 }
2689 }
2690
2691 return ret;
2692}
2693
2694API int
2695nc_server_ch_add_client(const char *name)
2696{
2697 uint16_t i;
2698 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002699
2700 if (!name) {
2701 ERRARG("name");
2702 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002703 }
2704
2705 /* WRITE LOCK */
2706 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2707
2708 /* check name uniqueness */
2709 for (i = 0; i < server_opts.ch_client_count; ++i) {
2710 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002711 ERR(NULL, "Call Home client \"%s\" already exists.", name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002712 /* WRITE UNLOCK */
2713 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2714 return -1;
2715 }
2716 }
2717
2718 ++server_opts.ch_client_count;
2719 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2720 if (!server_opts.ch_clients) {
2721 ERRMEM;
2722 /* WRITE UNLOCK */
2723 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2724 return -1;
2725 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002726 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002727
Michal Vasko93224072021-11-09 12:14:28 +01002728 client->name = strdup(name);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002729 client->id = ATOMIC_INC_RELAXED(server_opts.new_client_id);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002730 client->ch_endpts = NULL;
2731 client->ch_endpt_count = 0;
2732 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002733
Michal Vasko2e6defd2016-10-07 15:48:15 +02002734 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002735 client->start_with = NC_CH_FIRST_LISTED;
2736 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002737
Michal Vaskoadf30f02019-06-24 09:34:47 +02002738 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002739
2740 /* WRITE UNLOCK */
2741 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2742
2743 return 0;
2744}
2745
2746API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002747nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002748{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002749 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002750 int ret = -1;
2751
2752 /* WRITE LOCK */
2753 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2754
Michal Vaskoadf30f02019-06-24 09:34:47 +02002755 if (!name) {
2756 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002757 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002758 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002759
2760 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002761 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002762
2763 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002764 ret = 0;
2765 }
2766 free(server_opts.ch_clients);
2767 server_opts.ch_clients = NULL;
2768
2769 server_opts.ch_client_count = 0;
2770
2771 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002772 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002773 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002774 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko93224072021-11-09 12:14:28 +01002775 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002776
Michal Vasko2e6defd2016-10-07 15:48:15 +02002777 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002778 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002779
2780 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2781
2782 /* move last client and endpoint(s) to the empty space */
2783 --server_opts.ch_client_count;
2784 if (i < server_opts.ch_client_count) {
2785 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002786 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002787 } else if (!server_opts.ch_client_count) {
2788 free(server_opts.ch_clients);
2789 server_opts.ch_clients = NULL;
2790 }
2791
2792 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002793 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002794 }
2795 }
2796 }
2797
2798 /* WRITE UNLOCK */
2799 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2800
2801 return ret;
2802}
2803
2804API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002805nc_server_ch_is_client(const char *name)
2806{
2807 uint16_t i;
2808 int found = 0;
2809
2810 if (!name) {
2811 return found;
2812 }
2813
2814 /* READ LOCK */
2815 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2816
2817 /* check name uniqueness */
2818 for (i = 0; i < server_opts.ch_client_count; ++i) {
2819 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2820 found = 1;
2821 break;
2822 }
2823 }
2824
2825 /* UNLOCK */
2826 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2827
2828 return found;
2829}
2830
2831API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002832nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002833{
2834 uint16_t i;
2835 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002836 struct nc_ch_endpt *endpt;
2837 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002838
2839 if (!client_name) {
2840 ERRARG("client_name");
2841 return -1;
2842 } else if (!endpt_name) {
2843 ERRARG("endpt_name");
2844 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002845 } else if (!ti) {
2846 ERRARG("ti");
2847 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002848 }
2849
2850 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002851 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002852 if (!client) {
2853 return -1;
2854 }
2855
2856 for (i = 0; i < client->ch_endpt_count; ++i) {
2857 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002858 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002859 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002860 }
2861 }
2862
2863 ++client->ch_endpt_count;
2864 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2865 if (!client->ch_endpts) {
2866 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002867 goto cleanup;
2868 }
2869 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2870
2871 memset(endpt, 0, sizeof *client->ch_endpts);
Michal Vasko93224072021-11-09 12:14:28 +01002872 endpt->name = strdup(endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002873 endpt->ti = ti;
2874 endpt->sock_pending = -1;
2875 endpt->ka.idle_time = 1;
2876 endpt->ka.max_probes = 10;
2877 endpt->ka.probe_interval = 5;
2878
2879 switch (ti) {
2880#ifdef NC_ENABLED_SSH
2881 case NC_TI_LIBSSH:
2882 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2883 if (!endpt->opts.ssh) {
2884 ERRMEM;
2885 goto cleanup;
2886 }
roman41a11e42022-06-22 09:27:08 +02002887 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002888 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002889 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002890 break;
2891#endif
2892#ifdef NC_ENABLED_TLS
2893 case NC_TI_OPENSSL:
2894 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2895 if (!endpt->opts.tls) {
2896 ERRMEM;
2897 goto cleanup;
2898 }
2899 break;
2900#endif
2901 default:
2902 ERRINT;
2903 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002904 }
2905
Michal Vaskoadf30f02019-06-24 09:34:47 +02002906 /* success */
2907 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002908
Michal Vaskoadf30f02019-06-24 09:34:47 +02002909cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002910 /* UNLOCK */
2911 nc_server_ch_client_unlock(client);
2912
Michal Vaskoadf30f02019-06-24 09:34:47 +02002913 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002914}
2915
2916API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002917nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002918{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002919 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002920 struct nc_ch_client *client;
2921
2922 if (!client_name) {
2923 ERRARG("client_name");
2924 return -1;
2925 }
2926
2927 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002928 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002929 if (!client) {
2930 return -1;
2931 }
2932
Michal Vaskoadf30f02019-06-24 09:34:47 +02002933 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002934
2935 /* UNLOCK */
2936 nc_server_ch_client_unlock(client);
2937
2938 return ret;
2939}
2940
2941API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002942nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2943{
2944 uint16_t i;
2945 struct nc_ch_client *client = NULL;
2946 int found = 0;
2947
2948 if (!client_name || !endpt_name) {
2949 return found;
2950 }
2951
2952 /* READ LOCK */
2953 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2954
2955 for (i = 0; i < server_opts.ch_client_count; ++i) {
2956 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2957 client = &server_opts.ch_clients[i];
2958 break;
2959 }
2960 }
2961
2962 if (!client) {
2963 goto cleanup;
2964 }
2965
2966 for (i = 0; i < client->ch_endpt_count; ++i) {
2967 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2968 found = 1;
2969 break;
2970 }
2971 }
2972
2973cleanup:
2974 /* UNLOCK */
2975 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2976 return found;
2977}
2978
2979API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002980nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2981{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002982 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002983 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002984
2985 if (!client_name) {
2986 ERRARG("client_name");
2987 return -1;
2988 } else if (!endpt_name) {
2989 ERRARG("endpt_name");
2990 return -1;
2991 } else if (!address) {
2992 ERRARG("address");
2993 return -1;
2994 }
2995
2996 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002997 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2998 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002999 return -1;
3000 }
3001
Michal Vasko93224072021-11-09 12:14:28 +01003002 free(endpt->address);
3003 endpt->address = strdup(address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003004
3005 /* UNLOCK */
3006 nc_server_ch_client_unlock(client);
3007
Michal Vaskoadf30f02019-06-24 09:34:47 +02003008 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003009}
3010
3011API int
3012nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
3013{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003014 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003015 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003016
3017 if (!client_name) {
3018 ERRARG("client_name");
3019 return -1;
3020 } else if (!endpt_name) {
3021 ERRARG("endpt_name");
3022 return -1;
3023 } else if (!port) {
3024 ERRARG("port");
3025 return -1;
3026 }
3027
3028 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003029 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3030 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003031 return -1;
3032 }
3033
Michal Vaskoadf30f02019-06-24 09:34:47 +02003034 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003035
3036 /* UNLOCK */
3037 nc_server_ch_client_unlock(client);
3038
ravsz5c5a4422020-03-31 15:53:21 +02003039 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003040}
3041
3042API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003043nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
3044{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003045 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003046 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003047
3048 if (!client_name) {
3049 ERRARG("client_name");
3050 return -1;
3051 } else if (!endpt_name) {
3052 ERRARG("endpt_name");
3053 return -1;
3054 }
3055
3056 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003057 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3058 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003059 return -1;
3060 }
3061
Michal Vaskoadf30f02019-06-24 09:34:47 +02003062 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003063
3064 /* UNLOCK */
3065 nc_server_ch_client_unlock(client);
3066
Michal Vasko9af829a2019-09-12 13:50:00 +02003067 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003068}
3069
3070API int
3071nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
3072 int probe_interval)
3073{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003074 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003075 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003076
3077 if (!client_name) {
3078 ERRARG("client_name");
3079 return -1;
3080 } else if (!endpt_name) {
3081 ERRARG("endpt_name");
3082 return -1;
3083 }
3084
3085 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003086 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3087 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003088 return -1;
3089 }
3090
Michal Vaskoadf30f02019-06-24 09:34:47 +02003091 if (idle_time > -1) {
3092 endpt->ka.idle_time = idle_time;
3093 }
3094 if (max_probes > -1) {
3095 endpt->ka.max_probes = max_probes;
3096 }
3097 if (probe_interval > -1) {
3098 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003099 }
3100
3101 /* UNLOCK */
3102 nc_server_ch_client_unlock(client);
3103
Michal Vasko9af829a2019-09-12 13:50:00 +02003104 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003105}
3106
3107API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003108nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
3109{
3110 struct nc_ch_client *client;
3111
3112 if (!client_name) {
3113 ERRARG("client_name");
3114 return -1;
3115 } else if (!conn_type) {
3116 ERRARG("conn_type");
3117 return -1;
3118 }
3119
3120 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003121 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003122 if (!client) {
3123 return -1;
3124 }
3125
3126 if (client->conn_type != conn_type) {
3127 client->conn_type = conn_type;
3128
3129 /* set default options */
3130 switch (conn_type) {
3131 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003132 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003133 break;
3134 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003135 client->conn.period.period = 60;
3136 client->conn.period.anchor_time = 0;
3137 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003138 break;
3139 default:
3140 ERRINT;
3141 break;
3142 }
3143 }
3144
3145 /* UNLOCK */
3146 nc_server_ch_client_unlock(client);
3147
3148 return 0;
3149}
3150
3151API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003152nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
3153{
3154 struct nc_ch_client *client;
3155
3156 if (!client_name) {
3157 ERRARG("client_name");
3158 return -1;
3159 } else if (!period) {
3160 ERRARG("period");
3161 return -1;
3162 }
3163
3164 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003165 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003166 if (!client) {
3167 return -1;
3168 }
3169
3170 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003171 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003172 /* UNLOCK */
3173 nc_server_ch_client_unlock(client);
3174 return -1;
3175 }
3176
3177 client->conn.period.period = period;
3178
3179 /* UNLOCK */
3180 nc_server_ch_client_unlock(client);
3181
3182 return 0;
3183}
3184
3185API int
3186nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003187{
3188 struct nc_ch_client *client;
3189
3190 if (!client_name) {
3191 ERRARG("client_name");
3192 return -1;
3193 }
3194
3195 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003196 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003197 if (!client) {
3198 return -1;
3199 }
3200
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003201 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003202 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003203 /* UNLOCK */
3204 nc_server_ch_client_unlock(client);
3205 return -1;
3206 }
3207
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003208 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003209
3210 /* UNLOCK */
3211 nc_server_ch_client_unlock(client);
3212
3213 return 0;
3214}
3215
3216API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003217nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003218{
3219 struct nc_ch_client *client;
3220
3221 if (!client_name) {
3222 ERRARG("client_name");
3223 return -1;
3224 }
3225
3226 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003227 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003228 if (!client) {
3229 return -1;
3230 }
3231
3232 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003233 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003234 /* UNLOCK */
3235 nc_server_ch_client_unlock(client);
3236 return -1;
3237 }
3238
3239 client->conn.period.idle_timeout = idle_timeout;
3240
3241 /* UNLOCK */
3242 nc_server_ch_client_unlock(client);
3243
3244 return 0;
3245}
3246
3247API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003248nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
3249{
3250 struct nc_ch_client *client;
3251
3252 if (!client_name) {
3253 ERRARG("client_name");
3254 return -1;
3255 }
3256
3257 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003258 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003259 if (!client) {
3260 return -1;
3261 }
3262
3263 client->start_with = start_with;
3264
3265 /* UNLOCK */
3266 nc_server_ch_client_unlock(client);
3267
3268 return 0;
3269}
3270
3271API int
3272nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3273{
3274 struct nc_ch_client *client;
3275
3276 if (!client_name) {
3277 ERRARG("client_name");
3278 return -1;
3279 } else if (!max_attempts) {
3280 ERRARG("max_attempts");
3281 return -1;
3282 }
3283
3284 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003285 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003286 if (!client) {
3287 return -1;
3288 }
3289
3290 client->max_attempts = max_attempts;
3291
3292 /* UNLOCK */
3293 nc_server_ch_client_unlock(client);
3294
3295 return 0;
3296}
3297
Michal Vasko056f53c2022-10-21 13:38:15 +02003298/**
3299 * @brief Create a connection for an endpoint.
3300 *
3301 * Client lock is expected to be held.
3302 *
3303 * @param[in] endpt Endpoint to use.
3304 * @param[in] acquire_ctx_cb Callback for acquiring the libyang context.
3305 * @param[in] release_ctx_cb Callback for releasing the libyang context.
3306 * @param[in] ctx_cb_data Context callbacks data.
3307 * @param[out] session Created NC session.
3308 * @return NC_MSG values.
3309 */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003310static NC_MSG_TYPE
Michal Vasko58bac1c2022-03-24 15:25:26 +01003311nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
3312 nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01003313{
Michal Vasko71090fc2016-05-24 16:37:28 +02003314 NC_MSG_TYPE msgtype;
Michal Vasko58bac1c2022-03-24 15:25:26 +01003315 const struct ly_ctx *ctx = NULL;
Michal Vaskob05053d2016-01-22 16:12:06 +01003316 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003317 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003318 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003319
Michal Vasko056f53c2022-10-21 13:38:15 +02003320 sock = nc_sock_connect(endpt->address, endpt->port, NC_CH_CONNECT_TIMEOUT, &endpt->ka, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01003321 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02003322 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003323 }
3324
Michal Vasko93224072021-11-09 12:14:28 +01003325 /* acquire context */
3326 ctx = acquire_ctx_cb(ctx_cb_data);
3327 if (!ctx) {
3328 ERR(NULL, "Failed to acquire context for a new Call Home session.");
3329 close(sock);
3330 free(ip_host);
3331 return NC_MSG_ERROR;
3332 }
3333
3334 /* create session */
Michal Vasko131120a2018-05-29 15:44:02 +02003335 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003336 if (!(*session)) {
3337 ERRMEM;
3338 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003339 free(ip_host);
Michal Vaskod530d362022-09-07 10:07:57 +02003340 msgtype = NC_MSG_ERROR;
3341 goto fail;
Michal Vaskob05053d2016-01-22 16:12:06 +01003342 }
3343 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01003344 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003345 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01003346 (*session)->host = ip_host;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003347 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003348
Michal Vaskob05053d2016-01-22 16:12:06 +01003349 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003350#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003351 if (endpt->ti == NC_TI_LIBSSH) {
3352 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003353 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003354 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003355
Michal Vasko71090fc2016-05-24 16:37:28 +02003356 if (ret < 0) {
3357 msgtype = NC_MSG_ERROR;
3358 goto fail;
3359 } else if (!ret) {
3360 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003361 goto fail;
3362 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003363 } else
3364#endif
Radek Krejci53691be2016-02-22 13:58:37 +01003365#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003366 if (endpt->ti == NC_TI_OPENSSL) {
3367 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003368 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003369 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003370
Michal Vasko71090fc2016-05-24 16:37:28 +02003371 if (ret < 0) {
3372 msgtype = NC_MSG_ERROR;
3373 goto fail;
3374 } else if (!ret) {
3375 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003376 goto fail;
3377 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003378 } else
3379#endif
3380 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003381 ERRINT;
3382 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003383 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003384 goto fail;
3385 }
3386
3387 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02003388 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003389
3390 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003391 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003392 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003393 goto fail;
3394 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003395
roman6ece9c52022-06-22 09:29:17 +02003396 nc_gettimespec_mono_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02003397 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
roman6ece9c52022-06-22 09:29:17 +02003398 nc_gettimespec_real_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02003399 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003400 (*session)->status = NC_STATUS_RUNNING;
3401
Michal Vasko71090fc2016-05-24 16:37:28 +02003402 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003403
3404fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003405 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003406 *session = NULL;
Michal Vasko58bac1c2022-03-24 15:25:26 +01003407 if (ctx) {
3408 release_ctx_cb(ctx_cb_data);
3409 }
Michal Vasko71090fc2016-05-24 16:37:28 +02003410 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003411}
3412
Michal Vasko2e6defd2016-10-07 15:48:15 +02003413struct nc_ch_client_thread_arg {
3414 char *client_name;
Michal Vasko93224072021-11-09 12:14:28 +01003415 nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb;
3416 nc_server_ch_session_release_ctx_cb release_ctx_cb;
3417 void *ctx_cb_data;
3418 nc_server_ch_new_session_cb new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003419};
3420
3421static struct nc_ch_client *
3422nc_server_ch_client_with_endpt_lock(const char *name)
3423{
3424 struct nc_ch_client *client;
3425
3426 while (1) {
3427 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003428 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003429 if (!client) {
3430 return NULL;
3431 }
3432 if (client->ch_endpt_count) {
3433 return client;
3434 }
3435 /* no endpoints defined yet */
3436
3437 /* UNLOCK */
3438 nc_server_ch_client_unlock(client);
3439
3440 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3441 }
3442
3443 return NULL;
3444}
3445
3446static int
3447nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3448{
Michal Vasko3f05a092018-03-13 10:39:49 +01003449 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003450 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003451 struct timespec ts;
3452 struct nc_ch_client *client;
3453
Michal Vasko2e6defd2016-10-07 15:48:15 +02003454 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003455 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003456
Michal Vaskofeccb312022-03-24 15:24:59 +01003457 session->flags |= NC_SESSION_CH_THREAD;
Michal Vasko0db3db52021-03-03 10:45:42 +01003458
Michal Vasko2e6defd2016-10-07 15:48:15 +02003459 /* give the session to the user */
Michal Vasko93224072021-11-09 12:14:28 +01003460 if (data->new_session_cb(data->client_name, session)) {
Michal Vaskof1c26c22021-04-12 16:34:33 +02003461 /* something is wrong, free the session */
Michal Vaskofeccb312022-03-24 15:24:59 +01003462 session->flags &= ~NC_SESSION_CH_THREAD;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003463
3464 /* CH UNLOCK */
3465 pthread_mutex_unlock(&session->opts.server.ch_lock);
3466
Michal Vasko77d56d72022-09-07 10:30:48 +02003467 /* session terminated, free it and release its context */
Michal Vaskof1c26c22021-04-12 16:34:33 +02003468 nc_session_free(session, NULL);
Michal Vasko58bac1c2022-03-24 15:25:26 +01003469 data->release_ctx_cb(data->ctx_cb_data);
3470 return ret;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003471 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003472
3473 do {
roman6ece9c52022-06-22 09:29:17 +02003474 nc_gettimespec_real_add(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003475
Michal Vasko0db3db52021-03-03 10:45:42 +01003476 /* CH COND WAIT */
Michal Vaskoacf98472021-02-04 15:33:57 +01003477 r = pthread_cond_timedwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, &ts);
Michal Vasko3f05a092018-03-13 10:39:49 +01003478 if (!r) {
3479 /* we were woken up, something probably happened */
3480 if (session->status != NC_STATUS_RUNNING) {
3481 break;
3482 }
3483 } else if (r != ETIMEDOUT) {
Michal Vasko05532772021-06-03 12:12:38 +02003484 ERR(session, "Pthread condition timedwait failed (%s).", strerror(r));
Michal Vasko3f05a092018-03-13 10:39:49 +01003485 ret = -1;
3486 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003487 }
3488
Michal Vasko2e6defd2016-10-07 15:48:15 +02003489 /* check whether the client was not removed */
3490 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003491 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003492 if (!client) {
3493 /* client was removed, finish thread */
Michal Vasko05532772021-06-03 12:12:38 +02003494 VRB(session, "Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003495 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003496 ret = 1;
3497 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003498 }
3499
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003500 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003501 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003502 } else {
3503 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003504 }
3505
roman6ece9c52022-06-22 09:29:17 +02003506 nc_gettimespec_mono_add(&ts, 0);
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003507 if (!nc_session_get_notif_status(session) && idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
Michal Vasko05532772021-06-03 12:12:38 +02003508 VRB(session, "Call Home client \"%s\": session idle timeout elapsed.", client->name);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003509 session->status = NC_STATUS_INVALID;
3510 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3511 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003512
3513 /* UNLOCK */
3514 nc_server_ch_client_unlock(client);
3515
3516 } while (session->status == NC_STATUS_RUNNING);
3517
Michal Vaskofeccb312022-03-24 15:24:59 +01003518 /* signal to nc_session_free() that CH thread is terminating */
3519 session->flags &= ~NC_SESSION_CH_THREAD;
3520 pthread_cond_signal(&session->opts.server.ch_cond);
Michal Vasko0db3db52021-03-03 10:45:42 +01003521
Michal Vasko27377422018-03-15 08:59:35 +01003522 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003523 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01003524
Michal Vasko3f05a092018-03-13 10:39:49 +01003525 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003526}
3527
3528static void *
3529nc_ch_client_thread(void *arg)
3530{
3531 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3532 NC_MSG_TYPE msgtype;
3533 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003534 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003535 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003536 struct nc_ch_endpt *cur_endpt;
3537 struct nc_session *session;
3538 struct nc_ch_client *client;
Michal Vasko5d0a1e32022-09-07 07:46:36 +02003539 uint32_t client_id, reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003540
3541 /* LOCK */
3542 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3543 if (!client) {
3544 goto cleanup;
3545 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003546 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003547
3548 cur_endpt = &client->ch_endpts[0];
3549 cur_endpt_name = strdup(cur_endpt->name);
3550
3551 while (1) {
Michal Vasko056f53c2022-10-21 13:38:15 +02003552 if (!cur_attempts) {
3553 VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" connecting...", data->client_name, cur_endpt_name);
3554 }
Michal Vasko58bac1c2022-03-24 15:25:26 +01003555 msgtype = nc_connect_ch_endpt(cur_endpt, data->acquire_ctx_cb, data->release_ctx_cb, data->ctx_cb_data, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003556
3557 if (msgtype == NC_MSG_HELLO) {
3558 /* UNLOCK */
3559 nc_server_ch_client_unlock(client);
3560
Michal Vasko05532772021-06-03 12:12:38 +02003561 VRB(NULL, "Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003562 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3563 goto cleanup;
3564 }
Michal Vasko05532772021-06-03 12:12:38 +02003565 VRB(NULL, "Call Home client \"%s\" session terminated.", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003566
3567 /* LOCK */
3568 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3569 if (!client) {
3570 goto cleanup;
3571 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003572 if (client->id != client_id) {
3573 nc_server_ch_client_unlock(client);
3574 goto cleanup;
3575 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003576
3577 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003578 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003579 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko18e1fa02021-11-29 09:02:05 +01003580 if (client->conn.period.anchor_time) {
3581 /* anchored */
3582 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3583 } else {
3584 /* fixed timeout */
3585 reconnect_in = client->conn.period.period * 60;
3586 }
3587
Michal Vasko2e6defd2016-10-07 15:48:15 +02003588 /* UNLOCK */
3589 nc_server_ch_client_unlock(client);
3590
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003591 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
Michal Vasko75b836e2022-09-07 07:51:45 +02003592 VRB(NULL, "Call Home client \"%s\" reconnecting in %" PRIu32 " seconds.", data->client_name, reconnect_in);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003593 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003594
3595 /* LOCK */
3596 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3597 if (!client) {
3598 goto cleanup;
3599 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003600 if (client->id != client_id) {
3601 nc_server_ch_client_unlock(client);
3602 goto cleanup;
3603 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003604 }
3605
3606 /* set next endpoint to try */
3607 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003608 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003609 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003610 /* we keep the current one but due to unlock/lock we have to find it again */
3611 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3612 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3613 break;
3614 }
3615 }
3616 if (next_endpt_index >= client->ch_endpt_count) {
3617 /* endpoint was removed, start with the first one */
3618 next_endpt_index = 0;
3619 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003620 } else {
3621 /* just get a random index */
3622 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003623 }
3624
Michal Vasko2e6defd2016-10-07 15:48:15 +02003625 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003626 /* UNLOCK */
3627 nc_server_ch_client_unlock(client);
3628
Michal Vasko2e6defd2016-10-07 15:48:15 +02003629 /* session was not created */
Michal Vaskod8951c72022-09-26 09:39:29 +02003630 sleep(NC_CH_ENDPT_BACKOFF_WAIT);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003631
Michal Vasko6bb116b2016-10-26 13:53:46 +02003632 /* LOCK */
3633 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3634 if (!client) {
3635 goto cleanup;
3636 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003637 if (client->id != client_id) {
3638 nc_server_ch_client_unlock(client);
3639 goto cleanup;
3640 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003641
Michal Vasko2e6defd2016-10-07 15:48:15 +02003642 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003643
3644 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003645 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3646 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003647 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003648 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003649 }
3650
Peter Feiged05f2252018-09-03 08:09:47 +00003651 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003652 /* endpoint was removed, start with the first one */
Michal Vasko056f53c2022-10-21 13:38:15 +02003653 VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" removed.", data->client_name, cur_endpt_name);
Peter Feiged05f2252018-09-03 08:09:47 +00003654 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003655 cur_attempts = 0;
3656 } else if (cur_attempts == client->max_attempts) {
3657 /* we have tried to connect to this endpoint enough times */
Michal Vasko056f53c2022-10-21 13:38:15 +02003658 VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" failed connection attempt limit %" PRIu8 " reached.",
3659 data->client_name, cur_endpt_name, client->max_attempts);
3660
3661 /* clear a pending socket, if any */
3662 cur_endpt = &client->ch_endpts[next_endpt_index];
3663 if (cur_endpt->sock_pending > -1) {
3664 close(cur_endpt->sock_pending);
3665 cur_endpt->sock_pending = -1;
3666 }
3667
Peter Feiged05f2252018-09-03 08:09:47 +00003668 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003669 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003670 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003671 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003672 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003673 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003674 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003675 cur_attempts = 0;
3676 } /* else we keep the current one */
3677 }
Peter Feiged05f2252018-09-03 08:09:47 +00003678
3679 cur_endpt = &client->ch_endpts[next_endpt_index];
3680 free(cur_endpt_name);
3681 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003682 }
3683
3684cleanup:
Michal Vasko05532772021-06-03 12:12:38 +02003685 VRB(NULL, "Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003686 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003687 free(data->client_name);
3688 free(data);
3689 return NULL;
3690}
3691
3692API int
Michal Vasko93224072021-11-09 12:14:28 +01003693nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
3694 nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb)
Michal Vasko3f05a092018-03-13 10:39:49 +01003695{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003696 int ret;
3697 pthread_t tid;
3698 struct nc_ch_client_thread_arg *arg;
3699
3700 if (!client_name) {
3701 ERRARG("client_name");
3702 return -1;
Michal Vasko93224072021-11-09 12:14:28 +01003703 } else if (!acquire_ctx_cb) {
3704 ERRARG("acquire_ctx_cb");
3705 return -1;
3706 } else if (!release_ctx_cb) {
3707 ERRARG("release_ctx_cb");
3708 return -1;
3709 } else if (!new_session_cb) {
3710 ERRARG("new_session_cb");
Michal Vasko2e6defd2016-10-07 15:48:15 +02003711 return -1;
3712 }
3713
3714 arg = malloc(sizeof *arg);
3715 if (!arg) {
3716 ERRMEM;
3717 return -1;
3718 }
3719 arg->client_name = strdup(client_name);
3720 if (!arg->client_name) {
3721 ERRMEM;
3722 free(arg);
3723 return -1;
3724 }
Michal Vasko93224072021-11-09 12:14:28 +01003725 arg->acquire_ctx_cb = acquire_ctx_cb;
3726 arg->release_ctx_cb = release_ctx_cb;
3727 arg->ctx_cb_data = ctx_cb_data;
3728 arg->new_session_cb = new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003729
3730 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3731 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02003732 ERR(NULL, "Creating a new thread failed (%s).", strerror(ret));
Michal Vasko2e6defd2016-10-07 15:48:15 +02003733 free(arg->client_name);
3734 free(arg);
3735 return -1;
3736 }
3737 /* the thread now manages arg */
3738
3739 pthread_detach(tid);
3740
3741 return 0;
3742}
3743
Radek Krejci53691be2016-02-22 13:58:37 +01003744#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003745
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003746API time_t
3747nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003748{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003749 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003750 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003751 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003752 }
3753
Michal Vasko2e6defd2016-10-07 15:48:15 +02003754 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003755}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003756
3757API void
Michal Vasko71dbd772021-03-23 14:08:37 +01003758nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01003759{
3760 if (!session || (session->side != NC_SERVER)) {
3761 ERRARG("session");
3762 return;
3763 }
3764
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003765 /* NTF STATUS LOCK */
3766 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
3767
Michal Vasko71dbd772021-03-23 14:08:37 +01003768 ++session->opts.server.ntf_status;
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003769
3770 /* NTF STATUS UNLOCK */
3771 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko71dbd772021-03-23 14:08:37 +01003772}
3773
3774API void
3775nc_session_dec_notif_status(struct nc_session *session)
3776{
3777 if (!session || (session->side != NC_SERVER)) {
3778 ERRARG("session");
3779 return;
3780 }
3781
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003782 /* NTF STATUS LOCK */
3783 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
3784
Michal Vasko71dbd772021-03-23 14:08:37 +01003785 if (session->opts.server.ntf_status) {
3786 --session->opts.server.ntf_status;
3787 }
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003788
3789 /* NTF STATUS UNLOCK */
3790 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003791}
3792
3793API int
3794nc_session_get_notif_status(const struct nc_session *session)
3795{
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003796 uint32_t ntf_status;
3797
Michal Vasko3486a7c2017-03-03 13:28:07 +01003798 if (!session || (session->side != NC_SERVER)) {
3799 ERRARG("session");
3800 return 0;
3801 }
3802
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003803 /* NTF STATUS LOCK */
3804 pthread_mutex_lock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
3805
3806 ntf_status = session->opts.server.ntf_status;
3807
3808 /* NTF STATUS UNLOCK */
3809 pthread_mutex_unlock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
3810
3811 return ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003812}
Michal Vasko8f430592019-02-26 08:32:54 +01003813
3814API int
3815nc_session_is_callhome(const struct nc_session *session)
3816{
3817 if (!session || (session->side != NC_SERVER)) {
3818 ERRARG("session");
3819 return 0;
3820 }
3821
3822 if (session->flags & NC_SESSION_CALLHOME) {
3823 return 1;
3824 }
3825
3826 return 0;
3827}