blob: 398fcc7769047323d23aae4ed084a81e6da61142 [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>
romanf578cd52023-10-19 09:47:40 +020032#include <sys/stat.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020033#include <sys/types.h>
34#include <sys/un.h>
35#include <time.h>
36#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010037
romanf578cd52023-10-19 09:47:40 +020038#ifdef NC_ENABLED_SSH_TLS
39#include <curl/curl.h>
40#endif
41
Michal Vasko7a20d2e2021-05-19 16:40:23 +020042#include "compat.h"
romanf578cd52023-10-19 09:47:40 +020043#include "config.h"
44#include "log_p.h"
45#include "messages_p.h"
46#include "messages_server.h"
47#include "server_config_p.h"
48#include "session.h"
49#include "session_p.h"
Michal Vasko086311b2016-01-08 09:53:11 +010050#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020051#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010052
Michal Vaskob48aa812016-01-18 14:13:09 +010053struct nc_server_opts server_opts = {
romanf578cd52023-10-19 09:47:40 +020054 .config_lock = PTHREAD_RWLOCK_INITIALIZER,
55 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER,
Michal Vaskocf898172024-01-15 15:04:28 +010056 .idle_timeout = 180, /**< default idle timeout (not in config for UNIX socket) */
Michal Vaskob48aa812016-01-18 14:13:09 +010057};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010058
fanchanghu966f2de2016-07-21 02:28:57 -040059static nc_rpc_clb global_rpc_clb = NULL;
60
roman423cc0d2023-11-24 11:29:47 +010061#ifdef NC_ENABLED_SSH_TLS
Michal Vasko6f865982023-11-21 12:10:42 +010062/**
63 * @brief Lock CH client structures for reading and lock the specific client.
64 *
65 * @param[in] name Name of the CH client.
66 * @return CH client, NULL if not found.
67 */
68static struct nc_ch_client *
69nc_server_ch_client_lock(const char *name)
Michal Vasko3031aae2016-01-27 16:07:18 +010070{
71 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +020072 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020073
Michal Vasko6f865982023-11-21 12:10:42 +010074 assert(name);
Michal Vaskoddce1212019-05-24 09:58:49 +020075
Michal Vasko2e6defd2016-10-07 15:48:15 +020076 /* READ LOCK */
77 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
78
79 for (i = 0; i < server_opts.ch_client_count; ++i) {
romanf578cd52023-10-19 09:47:40 +020080 if (server_opts.ch_clients[i].name && !strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020081 client = &server_opts.ch_clients[i];
82 break;
83 }
84 }
85
86 if (!client) {
Michal Vaskoadf30f02019-06-24 09:34:47 +020087 /* READ UNLOCK */
88 pthread_rwlock_unlock(&server_opts.ch_client_lock);
89 } else {
90 /* CH CLIENT LOCK */
91 pthread_mutex_lock(&client->lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +020092 }
93
Michal Vasko6f865982023-11-21 12:10:42 +010094 return client;
Michal Vasko2e6defd2016-10-07 15:48:15 +020095}
96
Michal Vasko6f865982023-11-21 12:10:42 +010097/**
98 * @brief Unlock CH client strcutures and the specific client.
99 *
100 * @param[in] endpt Locked CH client structure.
101 */
102static void
Michal Vasko2e6defd2016-10-07 15:48:15 +0200103nc_server_ch_client_unlock(struct nc_ch_client *client)
104{
105 /* CH CLIENT UNLOCK */
106 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100107
108 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200109 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100110}
Michal Vasko086311b2016-01-08 09:53:11 +0100111
roman423cc0d2023-11-24 11:29:47 +0100112#endif /* NC_ENABLED_SSH_TLS */
113
roman78df0fa2023-11-02 10:33:57 +0100114int
115nc_server_get_referenced_endpt(const char *name, struct nc_endpt **endpt)
116{
117 uint16_t i;
118
119 for (i = 0; i < server_opts.endpt_count; i++) {
120 if (!strcmp(name, server_opts.endpts[i].name)) {
121 *endpt = &server_opts.endpts[i];
122 return 0;
123 }
124 }
125
126 ERR(NULL, "Referenced endpoint \"%s\" was not found.", name);
127 return 1;
128}
129
Michal Vasko1a38c862016-01-15 15:50:07 +0100130API void
131nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
132{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200133 if (!session) {
romanf578cd52023-10-19 09:47:40 +0200134 ERRARG(session, "session");
Michal Vasko45e53ae2016-04-07 11:46:03 +0200135 return;
136 } else if (!reason) {
romanf578cd52023-10-19 09:47:40 +0200137 ERRARG(session, "reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100138 return;
139 }
140
Michal Vasko142cfea2017-08-07 10:12:11 +0200141 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
142 session->killed_by = 0;
143 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100144 session->term_reason = reason;
145}
146
Michal Vasko142cfea2017-08-07 10:12:11 +0200147API void
148nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
149{
150 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
romanf578cd52023-10-19 09:47:40 +0200151 ERRARG(session, "session");
Michal Vasko142cfea2017-08-07 10:12:11 +0200152 return;
153 } else if (!sid) {
romanf578cd52023-10-19 09:47:40 +0200154 ERRARG(session, "sid");
Michal Vasko142cfea2017-08-07 10:12:11 +0200155 return;
156 }
157
158 session->killed_by = sid;
159}
160
161API void
162nc_session_set_status(struct nc_session *session, NC_STATUS status)
163{
164 if (!session) {
romanf578cd52023-10-19 09:47:40 +0200165 ERRARG(session, "session");
Michal Vasko142cfea2017-08-07 10:12:11 +0200166 return;
167 } else if (!status) {
romanf578cd52023-10-19 09:47:40 +0200168 ERRARG(session, "status");
Michal Vasko142cfea2017-08-07 10:12:11 +0200169 return;
170 }
171
172 session->status = status;
173}
174
romanf578cd52023-10-19 09:47:40 +0200175API int
176nc_server_init_ctx(struct ly_ctx **ctx)
177{
178 int new_ctx = 0, i, ret = 0;
179 struct lys_module *module;
180 /* all features */
181 const char *ietf_netconf_features[] = {"writable-running", "candidate", "rollback-on-error", "validate", "startup", "url", "xpath", "confirmed-commit", NULL};
182 /* all features (module has no features) */
183 const char *ietf_netconf_monitoring_features[] = {NULL};
184
185 NC_CHECK_ARG_RET(NULL, ctx, 1);
186
187 if (!*ctx) {
188 /* context not given, create a new one */
189 if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) {
190 ERR(NULL, "Couldn't create new libyang context.\n");
191 ret = 1;
192 goto cleanup;
193 }
194 new_ctx = 1;
195 }
196
197 if (new_ctx) {
198 /* new context created, implement both modules */
199 if (!ly_ctx_load_module(*ctx, "ietf-netconf", NULL, ietf_netconf_features)) {
200 ERR(NULL, "Loading module \"ietf-netconf\" failed.\n");
201 ret = 1;
202 goto cleanup;
203 }
204
205 if (!ly_ctx_load_module(*ctx, "ietf-netconf-monitoring", NULL, ietf_netconf_monitoring_features)) {
206 ERR(NULL, "Loading module \"ietf-netconf-monitoring\" failed.\n");
207 ret = 1;
208 goto cleanup;
209 }
210
211 goto cleanup;
212 }
213
214 module = ly_ctx_get_module_implemented(*ctx, "ietf-netconf");
215 if (module) {
216 /* ietf-netconf module is present, check features */
217 for (i = 0; ietf_netconf_features[i]; i++) {
218 if (lys_feature_value(module, ietf_netconf_features[i])) {
219 /* feature not found, enable all of them */
220 if (!ly_ctx_load_module(*ctx, "ietf-netconf", NULL, ietf_netconf_features)) {
221 ERR(NULL, "Loading module \"ietf-netconf\" failed.\n");
222 ret = 1;
223 goto cleanup;
224 }
225
226 break;
227 }
228 }
229 } else {
230 /* ietf-netconf module not found, add it */
231 if (!ly_ctx_load_module(*ctx, "ietf-netconf", NULL, ietf_netconf_features)) {
232 ERR(NULL, "Loading module \"ietf-netconf\" failed.\n");
233 ret = 1;
234 goto cleanup;
235 }
236 }
237
238 module = ly_ctx_get_module_implemented(*ctx, "ietf-netconf-monitoring");
239 if (!module) {
240 /* ietf-netconf-monitoring module not found, add it */
241 if (!ly_ctx_load_module(*ctx, "ietf-netconf-monitoring", NULL, ietf_netconf_monitoring_features)) {
242 ERR(NULL, "Loading module \"ietf-netconf-monitoring\" failed.\n");
243 ret = 1;
244 goto cleanup;
245 }
246 }
247
248cleanup:
249 if (new_ctx && ret) {
250 ly_ctx_destroy(*ctx);
251 *ctx = NULL;
252 }
253 return ret;
254}
255
roman96c27f92023-11-02 11:09:46 +0100256#ifdef NC_ENABLED_SSH_TLS
257
roman450c00b2023-11-02 10:31:45 +0100258API void
259nc_server_ch_set_dispatch_data(nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
260 nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb,
261 void *new_session_cb_data)
262{
263 NC_CHECK_ARG_RET(NULL, acquire_ctx_cb, release_ctx_cb, new_session_cb, );
264
265 server_opts.ch_dispatch_data.acquire_ctx_cb = acquire_ctx_cb;
266 server_opts.ch_dispatch_data.release_ctx_cb = release_ctx_cb;
267 server_opts.ch_dispatch_data.ctx_cb_data = ctx_cb_data;
268 server_opts.ch_dispatch_data.new_session_cb = new_session_cb;
269 server_opts.ch_dispatch_data.new_session_cb_data = new_session_cb_data;
270}
271
roman96c27f92023-11-02 11:09:46 +0100272#endif
273
Michal Vasko086311b2016-01-08 09:53:11 +0100274int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200275nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100276{
Michal Vasko06c860d2018-07-09 16:08:52 +0200277 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100278 int is_ipv4, sock;
279 struct sockaddr_storage saddr;
280
281 struct sockaddr_in *saddr4;
282 struct sockaddr_in6 *saddr6;
283
Michal Vasko086311b2016-01-08 09:53:11 +0100284 if (!strchr(address, ':')) {
285 is_ipv4 = 1;
286 } else {
287 is_ipv4 = 0;
288 }
289
290 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
291 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200292 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100293 goto fail;
294 }
295
Michal Vaskobe52dc22018-10-17 09:28:17 +0200296 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200297 opt = 1;
298 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200299 ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko06c860d2018-07-09 16:08:52 +0200300 goto fail;
301 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100302 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200303 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vasko83ad17e2019-01-30 10:11:37 +0100304 goto fail;
305 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200306
roman56acd552024-04-18 16:00:37 +0200307 /* configure keepalives */
308 if (nc_sock_configure_ka(sock, ka->enabled)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100309 goto fail;
310 }
roman56acd552024-04-18 16:00:37 +0200311 if (ka->enabled) {
312 if (nc_sock_configure_ka_idle_time(sock, ka->idle_time)) {
313 goto fail;
314 }
315 if (nc_sock_configure_ka_max_probes(sock, ka->max_probes)) {
316 goto fail;
317 }
318 if (nc_sock_configure_ka_probe_interval(sock, ka->probe_interval)) {
319 goto fail;
320 }
321 }
Michal Vasko086311b2016-01-08 09:53:11 +0100322
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200323 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100324 if (is_ipv4) {
325 saddr4 = (struct sockaddr_in *)&saddr;
326
327 saddr4->sin_family = AF_INET;
328 saddr4->sin_port = htons(port);
329
330 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200331 ERR(NULL, "Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100332 goto fail;
333 }
334
335 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200336 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100337 goto fail;
338 }
339
340 } else {
341 saddr6 = (struct sockaddr_in6 *)&saddr;
342
343 saddr6->sin6_family = AF_INET6;
344 saddr6->sin6_port = htons(port);
345
346 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200347 ERR(NULL, "Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100348 goto fail;
349 }
350
351 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200352 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100353 goto fail;
354 }
355 }
356
Michal Vaskofb89d772016-01-08 12:25:35 +0100357 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200358 ERR(NULL, "Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100359 goto fail;
360 }
Michal Vasko086311b2016-01-08 09:53:11 +0100361 return sock;
362
363fail:
364 if (sock > -1) {
365 close(sock);
366 }
367
368 return -1;
369}
370
Michal Vaskoc429a8e2024-01-15 15:04:57 +0100371/**
372 * @brief Create a listening socket (AF_UNIX).
373 *
374 * @param[in] opts The server options (unix permissions and address of the socket).
375 * @return Listening socket, -1 on error.
376 */
377static int
roman83683fb2023-02-24 09:15:23 +0100378nc_sock_listen_unix(const struct nc_server_unix_opts *opts)
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200379{
380 struct sockaddr_un sun;
381 int sock = -1;
382
roman83683fb2023-02-24 09:15:23 +0100383 if (strlen(opts->address) > sizeof(sun.sun_path) - 1) {
384 ERR(NULL, "Socket path \"%s\" is longer than maximum length %d.", opts->address, (int)(sizeof(sun.sun_path) - 1));
Michal Vasko93e96f12021-09-30 10:02:09 +0200385 goto fail;
386 }
387
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200388 sock = socket(AF_UNIX, SOCK_STREAM, 0);
389 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200390 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200391 goto fail;
392 }
393
394 memset(&sun, 0, sizeof(sun));
395 sun.sun_family = AF_UNIX;
roman83683fb2023-02-24 09:15:23 +0100396 snprintf(sun.sun_path, sizeof(sun.sun_path) - 1, "%s", opts->address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200397
398 unlink(sun.sun_path);
399 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
roman83683fb2023-02-24 09:15:23 +0100400 ERR(NULL, "Could not bind \"%s\" (%s).", opts->address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200401 goto fail;
402 }
403
404 if (opts->mode != (mode_t)-1) {
405 if (chmod(sun.sun_path, opts->mode) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200406 ERR(NULL, "Failed to set unix socket permissions (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200407 goto fail;
408 }
409 }
410
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200411 if ((opts->uid != (uid_t)-1) || (opts->gid != (gid_t)-1)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200412 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200413 ERR(NULL, "Failed to set unix socket uid/gid (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200414 goto fail;
415 }
416 }
417
418 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
roman83683fb2023-02-24 09:15:23 +0100419 ERR(NULL, "Unable to start listening on \"%s\" (%s).", opts->address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200420 goto fail;
421 }
422
423 return sock;
424
425fail:
426 if (sock > -1) {
427 close(sock);
428 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200429 return -1;
430}
431
aPiecek90ff0242021-02-14 14:58:01 +0100432/**
433 * @brief Evaluate socket name for AF_UNIX socket.
434 * @param[in] acc_sock_fd is file descriptor for the accepted socket (a nonnegative).
435 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
436 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
437 * @return 0 if the stream socket is unnamed. Parameter host is set to NULL.
438 * @return -1 in case of error. Parameter host is set to NULL.
439 */
440static int
441sock_host_unix(int acc_sock_fd, char **host)
442{
443 char *sun_path;
444 struct sockaddr_storage saddr;
445 socklen_t addr_len;
446
447 *host = NULL;
448 saddr.ss_family = AF_UNIX;
449 addr_len = sizeof(saddr);
450
451 if (getsockname(acc_sock_fd, (struct sockaddr *)&saddr, &addr_len)) {
Michal Vasko05532772021-06-03 12:12:38 +0200452 ERR(NULL, "getsockname failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100453 return -1;
454 }
455
456 sun_path = ((struct sockaddr_un *)&saddr)->sun_path;
457 if (!sun_path) {
458 /* stream socket is unnamed */
459 return 0;
460 }
461
roman3a95bb22023-10-26 11:07:17 +0200462 NC_CHECK_ERRMEM_RET(!(*host = strdup(sun_path)), -1);
aPiecek90ff0242021-02-14 14:58:01 +0100463
464 return 0;
465}
466
467/**
468 * @brief Evaluate socket name and port number for AF_INET socket.
469 * @param[in] addr is pointing to structure filled by accept function which was successful.
470 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
471 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
472 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
473 * @return -1 in case of error. Parameter host is set to NULL and port is unchanged.
474 */
475static int
476sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port)
477{
478 *host = malloc(INET_ADDRSTRLEN);
roman3a95bb22023-10-26 11:07:17 +0200479 NC_CHECK_ERRMEM_RET(!(*host), -1);
aPiecek90ff0242021-02-14 14:58:01 +0100480
aPiecek3da9b342021-02-18 15:00:03 +0100481 if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko69e98752022-12-14 14:20:17 +0100482 ERR(NULL, "inet_ntop failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100483 free(*host);
484 *host = NULL;
485 return -1;
486 }
487
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200488 *port = ntohs(addr->sin_port);
aPiecek90ff0242021-02-14 14:58:01 +0100489
490 return 0;
491}
492
493/**
494 * @brief Evaluate socket name and port number for AF_INET6 socket.
495 * @param[in] addr is pointing to structure filled by accept function which was successful.
496 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
497 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
498 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
499 * @return -1 in case of error. Parameter host is set to the NULL and port is unchanged.
500 */
501static int
502sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port)
503{
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200504 *host = malloc(INET6_ADDRSTRLEN);
roman3a95bb22023-10-26 11:07:17 +0200505 NC_CHECK_ERRMEM_RET(!(*host), -1);
aPiecek90ff0242021-02-14 14:58:01 +0100506
aPiecek3da9b342021-02-18 15:00:03 +0100507 if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko69e98752022-12-14 14:20:17 +0100508 ERR(NULL, "inet_ntop failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100509 free(*host);
510 *host = NULL;
511 return -1;
512 }
513
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200514 *port = ntohs(addr->sin6_port);
aPiecek90ff0242021-02-14 14:58:01 +0100515
516 return 0;
517}
518
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200519int
Michal Vasko6f865982023-11-21 12:10:42 +0100520nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, pthread_mutex_t *bind_lock, int timeout, char **host,
521 uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100522{
Michal Vaskof54cd352017-02-22 13:42:02 +0100523 sigset_t sigmask, origmask;
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200524 uint16_t i, j, pfd_count, client_port;
525 char *client_address;
Michal Vasko086311b2016-01-08 09:53:11 +0100526 struct pollfd *pfd;
527 struct sockaddr_storage saddr;
528 socklen_t saddr_len = sizeof(saddr);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200529 int ret, client_sock, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100530
531 pfd = malloc(bind_count * sizeof *pfd);
roman3a95bb22023-10-26 11:07:17 +0200532 NC_CHECK_ERRMEM_RET(!pfd, -1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100533
romanf578cd52023-10-19 09:47:40 +0200534 /* LOCK */
535 pthread_mutex_lock(bind_lock);
536
Michal Vaskoac2f6182017-01-30 14:32:03 +0100537 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200538 if (binds[i].sock < 0) {
539 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200540 continue;
541 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200542 if (binds[i].pollin) {
543 binds[i].pollin = 0;
544 /* leftover pollin */
545 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100546 break;
547 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100548 pfd[pfd_count].fd = binds[i].sock;
549 pfd[pfd_count].events = POLLIN;
550 pfd[pfd_count].revents = 0;
551
552 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100553 }
554
Michal Vasko0a3f3752016-10-13 14:58:38 +0200555 if (sock == -1) {
556 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100557 sigfillset(&sigmask);
558 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100559 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100560 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
561
Michal Vasko0a3f3752016-10-13 14:58:38 +0200562 if (!ret) {
563 /* we timeouted */
564 free(pfd);
romanf578cd52023-10-19 09:47:40 +0200565 /* UNLOCK */
566 pthread_mutex_unlock(bind_lock);
Michal Vasko0a3f3752016-10-13 14:58:38 +0200567 return 0;
568 } else if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200569 ERR(NULL, "Poll failed (%s).", strerror(errno));
Michal Vasko0a3f3752016-10-13 14:58:38 +0200570 free(pfd);
romanf578cd52023-10-19 09:47:40 +0200571 /* UNLOCK */
572 pthread_mutex_unlock(bind_lock);
Michal Vasko0a3f3752016-10-13 14:58:38 +0200573 return -1;
574 }
Michal Vasko086311b2016-01-08 09:53:11 +0100575
Michal Vaskoac2f6182017-01-30 14:32:03 +0100576 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
577 /* adjust i so that indices in binds and pfd always match */
578 while (binds[i].sock != pfd[j].fd) {
579 ++i;
580 }
581
582 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200583 --ret;
584
585 if (!ret) {
586 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100587 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200588 break;
589 } else {
590 /* just remember the event for next time */
591 binds[i].pollin = 1;
592 }
593 }
Michal Vasko086311b2016-01-08 09:53:11 +0100594 }
595 }
596 free(pfd);
Michal Vasko086311b2016-01-08 09:53:11 +0100597 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100598 ERRINT;
romanf578cd52023-10-19 09:47:40 +0200599 /* UNLOCK */
600 pthread_mutex_unlock(bind_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100601 return -1;
602 }
603
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200604 /* accept connection */
605 client_sock = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
606 if (client_sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200607 ERR(NULL, "Accept failed (%s).", strerror(errno));
romanf578cd52023-10-19 09:47:40 +0200608 /* UNLOCK */
609 pthread_mutex_unlock(bind_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100610 return -1;
611 }
612
Michal Vasko0190bc32016-03-02 15:47:49 +0100613 /* make the socket non-blocking */
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200614 if (((flags = fcntl(client_sock, F_GETFL)) == -1) || (fcntl(client_sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200615 ERR(NULL, "Fcntl failed (%s).", strerror(errno));
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200616 goto fail;
Michal Vasko0190bc32016-03-02 15:47:49 +0100617 }
618
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200619 /* learn information about the client end */
620 if (saddr.ss_family == AF_UNIX) {
621 if (sock_host_unix(client_sock, &client_address)) {
622 goto fail;
623 }
624 client_port = 0;
625 } else if (saddr.ss_family == AF_INET) {
626 if (sock_host_inet((struct sockaddr_in *)&saddr, &client_address, &client_port)) {
627 goto fail;
628 }
629 } else if (saddr.ss_family == AF_INET6) {
630 if (sock_host_inet6((struct sockaddr_in6 *)&saddr, &client_address, &client_port)) {
631 goto fail;
632 }
633 } else {
634 ERR(NULL, "Source host of an unknown protocol family.");
635 goto fail;
aPiecek90ff0242021-02-14 14:58:01 +0100636 }
Michal Vasko086311b2016-01-08 09:53:11 +0100637
aPiecek90ff0242021-02-14 14:58:01 +0100638 if (saddr.ss_family == AF_UNIX) {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200639 VRB(NULL, "Accepted a connection on %s.", binds[i].address);
aPiecek90ff0242021-02-14 14:58:01 +0100640 } else {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200641 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 +0100642 }
643
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200644 if (host) {
645 *host = client_address;
646 } else {
647 free(client_address);
648 }
649 if (port) {
650 *port = client_port;
651 }
652 if (idx) {
653 *idx = i;
654 }
romanf578cd52023-10-19 09:47:40 +0200655 /* UNLOCK */
656 pthread_mutex_unlock(bind_lock);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200657 return client_sock;
658
659fail:
660 close(client_sock);
romanf578cd52023-10-19 09:47:40 +0200661 /* UNLOCK */
662 pthread_mutex_unlock(bind_lock);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200663 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100664}
665
Michal Vasko238b6c12021-12-14 15:14:09 +0100666API struct nc_server_reply *
Michal Vasko05532772021-06-03 12:12:38 +0200667nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100668{
Michal Vasko77367452021-02-16 16:32:18 +0100669 const char *identifier = NULL, *revision = NULL, *format = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100670 char *model_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100671 struct ly_out *out;
Michal Vasko9b1a9522021-03-15 16:24:26 +0100672 const struct lys_module *module = NULL, *mod;
Michal Vasko77367452021-02-16 16:32:18 +0100673 const struct lysp_submodule *submodule = NULL;
674 struct lyd_node *child, *err, *data = NULL;
675 LYS_OUTFORMAT outformat = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100676
Michal Vasko77367452021-02-16 16:32:18 +0100677 LY_LIST_FOR(lyd_child(rpc), child) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100678 if (!strcmp(child->schema->name, "identifier")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200679 identifier = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100680 } else if (!strcmp(child->schema->name, "version")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200681 revision = lyd_get_value(child);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200682 if (revision && (revision[0] == '\0')) {
Michal Vasko77367452021-02-16 16:32:18 +0100683 revision = NULL;
Radek Krejci1afa7792017-03-26 11:24:16 -0500684 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100685 } else if (!strcmp(child->schema->name, "format")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200686 format = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100687 }
688 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200689 VRB(session, "Module \"%s@%s\" was requested.", identifier, revision ? revision : "<any>");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100690
Michal Vasko77367452021-02-16 16:32:18 +0100691 /* check revision */
692 if (revision && (strlen(revision) != 10) && strcmp(revision, "1.0")) {
Michal Vasko93224072021-11-09 12:14:28 +0100693 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100694 nc_err_set_msg(err, "The requested version is not supported.", "en");
695 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100696 }
697
Michal Vasko77367452021-02-16 16:32:18 +0100698 if (revision) {
699 /* get specific module */
Michal Vasko93224072021-11-09 12:14:28 +0100700 module = ly_ctx_get_module(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100701 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100702 submodule = ly_ctx_get_submodule(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100703 }
704 } else {
705 /* try to get implemented, then latest module */
Michal Vasko93224072021-11-09 12:14:28 +0100706 module = ly_ctx_get_module_implemented(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100707 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100708 module = ly_ctx_get_module_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100709 }
710 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100711 submodule = ly_ctx_get_submodule_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100712 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200713 }
Michal Vasko77367452021-02-16 16:32:18 +0100714 if (!module && !submodule) {
Michal Vasko93224072021-11-09 12:14:28 +0100715 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko5ca5d972022-09-14 13:51:31 +0200716 nc_err_set_msg(err, "The requested module was not found.", "en");
Michal Vasko1a38c862016-01-15 15:50:07 +0100717 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100718 }
719
720 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100721 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko77367452021-02-16 16:32:18 +0100722 outformat = LYS_OUT_YANG;
Radek Krejci90fba642016-12-07 15:59:45 +0100723 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko77367452021-02-16 16:32:18 +0100724 outformat = LYS_OUT_YIN;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100725 } else {
Michal Vasko93224072021-11-09 12:14:28 +0100726 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100727 nc_err_set_msg(err, "The requested format is not supported.", "en");
728 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100729 }
Michal Vasko77367452021-02-16 16:32:18 +0100730
731 /* print */
732 ly_out_new_memory(&model_data, 0, &out);
733 if (module) {
734 lys_print_module(out, module, outformat, 0, 0);
735 } else {
736 lys_print_submodule(out, submodule, outformat, 0, 0);
737 }
738 ly_out_free(out, NULL, 0);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200739 if (!model_data) {
740 ERRINT;
741 return NULL;
742 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100743
Michal Vasko9b1a9522021-03-15 16:24:26 +0100744 /* create reply */
Michal Vasko93224072021-11-09 12:14:28 +0100745 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vasko9b1a9522021-03-15 16:24:26 +0100746 if (!mod || lyd_new_inner(NULL, mod, "get-schema", 0, &data)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100747 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200748 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100749 return NULL;
750 }
Michal Vasko58791da2024-02-26 13:52:59 +0100751 if (lyd_new_any(data, NULL, "data", model_data, LYD_ANYDATA_STRING, LYD_NEW_ANY_USE_VALUE | LYD_NEW_VAL_OUTPUT, NULL)) {
Michal Vasko9b1a9522021-03-15 16:24:26 +0100752 ERRINT;
Michal Vaskoa50f68e2022-02-24 16:10:54 +0100753 free(model_data);
Michal Vasko9b1a9522021-03-15 16:24:26 +0100754 lyd_free_tree(data);
755 return NULL;
756 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100757
Radek Krejci36dfdb32016-09-01 16:56:35 +0200758 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100759}
760
Michal Vasko238b6c12021-12-14 15:14:09 +0100761API struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100762nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100763{
Michal Vasko428087d2016-01-14 16:04:28 +0100764 session->term_reason = NC_SESSION_TERM_CLOSED;
765 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100766}
767
Michal Vasko93224072021-11-09 12:14:28 +0100768/**
769 * @brief Initialize a context with default RPC callbacks if none are set.
770 *
771 * @param[in] ctx Context to initialize.
772 */
773static void
romanf578cd52023-10-19 09:47:40 +0200774nc_server_init_cb_ctx(const struct ly_ctx *ctx)
Michal Vasko086311b2016-01-08 09:53:11 +0100775{
Michal Vasko77367452021-02-16 16:32:18 +0100776 struct lysc_node *rpc;
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100777
Michal Vasko238b6c12021-12-14 15:14:09 +0100778 if (global_rpc_clb) {
779 /* expect it to handle these RPCs as well */
780 return;
781 }
782
Michal Vasko05ba9df2016-01-13 14:40:27 +0100783 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100784 rpc = NULL;
785 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
786 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
787 }
Michal Vasko88639e92017-08-03 14:38:10 +0200788 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100789 rpc->priv = nc_clb_default_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100790 }
791
Michal Vasko93224072021-11-09 12:14:28 +0100792 /* set default <close-session> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100793 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200794 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100795 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100796 }
Michal Vasko93224072021-11-09 12:14:28 +0100797}
Michal Vasko05ba9df2016-01-13 14:40:27 +0100798
Michal Vasko93224072021-11-09 12:14:28 +0100799API int
800nc_server_init(void)
801{
Michal Vasko29f2f022024-03-13 09:06:48 +0100802 pthread_rwlockattr_t *attr_p = NULL;
Michal Vasko93224072021-11-09 12:14:28 +0100803 int r;
804
romand82caf12024-03-05 14:21:39 +0100805 ATOMIC_STORE_RELAXED(server_opts.new_session_id, 1);
806 ATOMIC_STORE_RELAXED(server_opts.new_client_id, 1);
Michal Vaskob48aa812016-01-18 14:13:09 +0100807
Michal Vasko93224072021-11-09 12:14:28 +0100808#ifdef HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
Michal Vasko29f2f022024-03-13 09:06:48 +0100809 pthread_rwlockattr_t attr;
810
Michal Vasko93224072021-11-09 12:14:28 +0100811 if ((r = pthread_rwlockattr_init(&attr))) {
812 ERR(NULL, "%s: failed init attribute (%s).", __func__, strerror(r));
813 goto error;
814 }
815 attr_p = &attr;
816 if ((r = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))) {
817 ERR(NULL, "%s: failed set attribute (%s).", __func__, strerror(r));
818 goto error;
819 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700820#endif
Michal Vasko93224072021-11-09 12:14:28 +0100821
romanf578cd52023-10-19 09:47:40 +0200822 if ((r = pthread_rwlock_init(&server_opts.config_lock, attr_p))) {
Michal Vasko93224072021-11-09 12:14:28 +0100823 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
824 goto error;
825 }
826 if ((r = pthread_rwlock_init(&server_opts.ch_client_lock, attr_p))) {
827 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
828 goto error;
829 }
830
831 if (attr_p) {
832 pthread_rwlockattr_destroy(attr_p);
Frank Rimpler9f838b02018-07-25 06:44:03 +0000833 }
romanf578cd52023-10-19 09:47:40 +0200834
835#ifdef NC_ENABLED_SSH_TLS
836 if (curl_global_init(CURL_GLOBAL_SSL | CURL_GLOBAL_ACK_EINTR)) {
837 ERR(NULL, "%s: failed to init CURL.", __func__);
838 goto error;
839 }
840#endif
841
842 if ((r = pthread_mutex_init(&server_opts.bind_lock, NULL))) {
843 ERR(NULL, "%s: failed to init bind lock(%s).", __func__, strerror(r));
844 goto error;
845 }
846
Michal Vasko086311b2016-01-08 09:53:11 +0100847 return 0;
Michal Vasko93224072021-11-09 12:14:28 +0100848
849error:
850 if (attr_p) {
851 pthread_rwlockattr_destroy(attr_p);
852 }
853 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100854}
855
Michal Vaskob48aa812016-01-18 14:13:09 +0100856API void
857nc_server_destroy(void)
858{
romana2ff4ef2024-01-19 14:41:46 +0100859 uint32_t i, endpt_count;
Radek Krejci658782b2016-12-04 22:04:55 +0100860
861 for (i = 0; i < server_opts.capabilities_count; i++) {
Michal Vasko93224072021-11-09 12:14:28 +0100862 free(server_opts.capabilities[i]);
Radek Krejci658782b2016-12-04 22:04:55 +0100863 }
864 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200865 server_opts.capabilities = NULL;
866 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200867 if (server_opts.content_id_data && server_opts.content_id_data_free) {
868 server_opts.content_id_data_free(server_opts.content_id_data);
869 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200870
romanf578cd52023-10-19 09:47:40 +0200871 nc_server_config_listen(NULL, NC_OP_DELETE);
872 nc_server_config_ch(NULL, NC_OP_DELETE);
873
romana2ff4ef2024-01-19 14:41:46 +0100874 endpt_count = server_opts.endpt_count;
875 for (i = 0; i < endpt_count; i++) {
876 if (server_opts.endpts[i].ti == NC_TI_UNIX) {
877 _nc_server_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]);
878 }
879 }
880
romanf578cd52023-10-19 09:47:40 +0200881 pthread_mutex_destroy(&server_opts.bind_lock);
882
883#ifdef NC_ENABLED_SSH_TLS
romana9ec3362023-12-21 10:59:57 +0100884 free(server_opts.authkey_path_fmt);
885 server_opts.authkey_path_fmt = NULL;
roman808f3f62023-11-23 16:01:04 +0100886 free(server_opts.pam_config_name);
887 server_opts.pam_config_name = NULL;
Michal Vasko1c2d2652023-10-17 08:53:36 +0200888 if (server_opts.interactive_auth_data && server_opts.interactive_auth_data_free) {
889 server_opts.interactive_auth_data_free(server_opts.interactive_auth_data);
890 }
891 server_opts.interactive_auth_data = NULL;
892 server_opts.interactive_auth_data_free = NULL;
893
romanf578cd52023-10-19 09:47:40 +0200894 nc_server_config_ks_keystore(NULL, NC_OP_DELETE);
895 nc_server_config_ts_truststore(NULL, NC_OP_DELETE);
896 curl_global_cleanup();
897#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskob48aa812016-01-18 14:13:09 +0100898}
899
Michal Vasko086311b2016-01-08 09:53:11 +0100900API int
901nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
902{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200903 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
romanf578cd52023-10-19 09:47:40 +0200904 ERRARG(NULL, "basic_mode");
Michal Vasko45e53ae2016-04-07 11:46:03 +0200905 return -1;
906 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
romanf578cd52023-10-19 09:47:40 +0200907 ERRARG(NULL, "also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100908 return -1;
909 }
910
romanf578cd52023-10-19 09:47:40 +0200911 ATOMIC_STORE_RELAXED(server_opts.wd_basic_mode, basic_mode);
912 ATOMIC_STORE_RELAXED(server_opts.wd_also_supported, also_supported);
Michal Vasko086311b2016-01-08 09:53:11 +0100913 return 0;
914}
915
Michal Vasko1a38c862016-01-15 15:50:07 +0100916API void
Michal Vasko55f03972016-04-13 08:56:01 +0200917nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
918{
919 if (!basic_mode && !also_supported) {
romanf578cd52023-10-19 09:47:40 +0200920 ERRARG(NULL, "basic_mode and also_supported");
Michal Vasko55f03972016-04-13 08:56:01 +0200921 return;
922 }
923
924 if (basic_mode) {
romanf578cd52023-10-19 09:47:40 +0200925 *basic_mode = ATOMIC_LOAD_RELAXED(server_opts.wd_basic_mode);
Michal Vasko55f03972016-04-13 08:56:01 +0200926 }
927 if (also_supported) {
romanf578cd52023-10-19 09:47:40 +0200928 *also_supported = ATOMIC_LOAD_RELAXED(server_opts.wd_also_supported);
Michal Vasko55f03972016-04-13 08:56:01 +0200929 }
930}
931
Michal Vasko55f03972016-04-13 08:56:01 +0200932API int
Radek Krejci658782b2016-12-04 22:04:55 +0100933nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200934{
Michal Vasko93224072021-11-09 12:14:28 +0100935 void *mem;
Radek Krejci658782b2016-12-04 22:04:55 +0100936
937 if (!value || !value[0]) {
romanf578cd52023-10-19 09:47:40 +0200938 ERRARG(NULL, "value must not be empty");
Radek Krejci658782b2016-12-04 22:04:55 +0100939 return EXIT_FAILURE;
940 }
941
Michal Vasko93224072021-11-09 12:14:28 +0100942 mem = realloc(server_opts.capabilities, (server_opts.capabilities_count + 1) * sizeof *server_opts.capabilities);
roman3a95bb22023-10-26 11:07:17 +0200943 NC_CHECK_ERRMEM_RET(!mem, EXIT_FAILURE);
Michal Vasko93224072021-11-09 12:14:28 +0100944 server_opts.capabilities = mem;
945
946 server_opts.capabilities[server_opts.capabilities_count] = strdup(value);
947 server_opts.capabilities_count++;
Radek Krejci658782b2016-12-04 22:04:55 +0100948
949 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200950}
951
Michal Vasko1a38c862016-01-15 15:50:07 +0100952API void
Michal Vasko1440a742021-03-31 11:11:03 +0200953nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
954 void (*free_user_data)(void *user_data))
955{
956 server_opts.content_id_clb = content_id_clb;
957 server_opts.content_id_data = user_data;
958 server_opts.content_id_data_free = free_user_data;
959}
960
Michal Vasko71090fc2016-05-24 16:37:28 +0200961API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +0100962nc_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 +0100963{
Michal Vasko71090fc2016-05-24 16:37:28 +0200964 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200965 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200966
romand82caf12024-03-05 14:21:39 +0100967 NC_CHECK_ARG_RET(NULL, ctx, username, fdin >= 0, fdout >= 0, session, NC_MSG_ERROR);
romanf578cd52023-10-19 09:47:40 +0200968
romand82caf12024-03-05 14:21:39 +0100969 NC_CHECK_SRV_INIT_RET(NC_MSG_ERROR);
Michal Vasko086311b2016-01-08 09:53:11 +0100970
Michal Vasko93224072021-11-09 12:14:28 +0100971 /* init ctx as needed */
romanf578cd52023-10-19 09:47:40 +0200972 nc_server_init_cb_ctx(ctx);
Michal Vasko93224072021-11-09 12:14:28 +0100973
Michal Vasko086311b2016-01-08 09:53:11 +0100974 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200975 *session = nc_new_session(NC_SERVER, 0);
roman3a95bb22023-10-26 11:07:17 +0200976 NC_CHECK_ERRMEM_RET(!(*session), NC_MSG_ERROR);
Michal Vasko1a38c862016-01-15 15:50:07 +0100977 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100978
Michal Vasko086311b2016-01-08 09:53:11 +0100979 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100980 (*session)->ti_type = NC_TI_FD;
981 (*session)->ti.fd.in = fdin;
982 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100983
Michal Vasko93224072021-11-09 12:14:28 +0100984 /* assign context */
Michal Vasko1a38c862016-01-15 15:50:07 +0100985 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +0100986 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100987
Michal Vaskob48aa812016-01-18 14:13:09 +0100988 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200989 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100990
Michal Vasko086311b2016-01-08 09:53:11 +0100991 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200992 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200993 if (msgtype != NC_MSG_HELLO) {
994 nc_session_free(*session, NULL);
995 *session = NULL;
996 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100997 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200998
Michal Vaskod8a74192023-02-06 15:51:50 +0100999 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02001000 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +01001001 nc_realtime_get(&ts_cur);
roman44efa322023-11-03 13:57:25 +01001002 (*session)->opts.server.session_start = ts_cur;
Michal Vasko9fb42272017-10-05 13:50:05 +02001003
Michal Vasko1a38c862016-01-15 15:50:07 +01001004 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +01001005
Michal Vasko71090fc2016-05-24 16:37:28 +02001006 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001007}
Michal Vasko9e036d52016-01-08 10:49:26 +01001008
Michal Vaskob30b99c2016-07-26 11:35:43 +02001009static void
Michal Vasko74c345f2018-02-07 10:37:11 +01001010nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
1011{
1012 uint8_t q_last;
1013
1014 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
1015 ERRINT;
1016 return;
1017 }
1018
1019 /* get a unique queue value (by adding 1 to the last added value, if any) */
1020 if (ps->queue_len) {
1021 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
1022 *id = ps->queue[q_last] + 1;
1023 } else {
1024 *id = 0;
1025 }
1026
1027 /* add the id into the queue */
1028 ++ps->queue_len;
1029 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
1030 ps->queue[q_last] = *id;
1031}
1032
1033static void
Michal Vaskob30b99c2016-07-26 11:35:43 +02001034nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
1035{
Michal Vasko74c345f2018-02-07 10:37:11 +01001036 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001037
1038 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +01001039 /* get the actual queue idx */
1040 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001041
1042 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +01001043 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +02001044 /* another equal value, simply cannot be */
1045 ERRINT;
1046 }
Michal Vaskod8340032018-02-12 14:41:00 +01001047 if (found == 2) {
1048 /* move the following values */
1049 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
1050 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001051 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +02001052 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +01001053 if (i == 0) {
1054 found = 1;
1055 } else {
1056 /* this is not okay, our id is in the middle of the queue */
1057 found = 2;
1058 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001059 }
1060 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001061 if (!found) {
1062 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +01001063 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001064 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001065
Michal Vasko103fe632018-02-12 16:37:45 +01001066 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +01001067 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +01001068 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +01001069 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
1070 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001071}
1072
Michal Vaskof04a52a2016-04-07 10:52:10 +02001073int
Michal Vasko26043172016-07-26 14:08:59 +02001074nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001075{
1076 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001077 struct timespec ts;
1078
Michal Vaskobe86fe32016-04-07 10:43:03 +02001079 /* LOCK */
Michal Vasko8c7def52023-06-06 14:48:56 +02001080 ret = pthread_mutex_lock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001081 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001082 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001083 return -1;
1084 }
1085
Michal Vasko74c345f2018-02-07 10:37:11 +01001086 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +01001087 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko05532772021-06-03 12:12:38 +02001088 ERR(NULL, "%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +01001089 pthread_mutex_unlock(&ps->lock);
1090 return -1;
1091 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001092
1093 /* add ourselves into the queue */
1094 nc_ps_queue_add_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001095 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 +02001096 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001097
1098 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001099 while (ps->queue[ps->queue_begin] != *id) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001100 nc_timeouttime_get(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001101
Michal Vaskod8a74192023-02-06 15:51:50 +01001102 ret = pthread_cond_clockwait(&ps->cond, &ps->lock, COMPAT_CLOCK_ID, &ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001103 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301104 /**
1105 * This may happen when another thread releases the lock and broadcasts the condition
1106 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1107 * but when actually this thread was ready for condition.
1108 */
preetbhansali629dfc42018-12-17 16:04:40 +05301109 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301110 break;
1111 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001112
Michal Vasko05532772021-06-03 12:12:38 +02001113 ERR(NULL, "%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001114 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001115 nc_ps_queue_remove_id(ps, *id);
1116 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001117 return -1;
1118 }
1119 }
1120
Michal Vaskobe86fe32016-04-07 10:43:03 +02001121 /* UNLOCK */
1122 pthread_mutex_unlock(&ps->lock);
1123
1124 return 0;
1125}
1126
Michal Vaskof04a52a2016-04-07 10:52:10 +02001127int
Michal Vasko26043172016-07-26 14:08:59 +02001128nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001129{
1130 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001131
1132 /* LOCK */
Michal Vasko8c7def52023-06-06 14:48:56 +02001133 ret = pthread_mutex_lock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001134 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001135 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001136 ret = -1;
1137 }
1138
Michal Vaskob30b99c2016-07-26 11:35:43 +02001139 /* we must be the first, it was our turn after all, right? */
1140 if (ps->queue[ps->queue_begin] != id) {
1141 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001142 /* UNLOCK */
1143 if (!ret) {
1144 pthread_mutex_unlock(&ps->lock);
1145 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001146 return -1;
1147 }
1148
Michal Vaskobe86fe32016-04-07 10:43:03 +02001149 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001150 nc_ps_queue_remove_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001151 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 +02001152 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001153
1154 /* broadcast to all other threads that the queue moved */
1155 pthread_cond_broadcast(&ps->cond);
1156
Michal Vaskobe86fe32016-04-07 10:43:03 +02001157 /* UNLOCK */
1158 if (!ret) {
1159 pthread_mutex_unlock(&ps->lock);
1160 }
1161
1162 return ret;
1163}
1164
Michal Vasko428087d2016-01-14 16:04:28 +01001165API struct nc_pollsession *
1166nc_ps_new(void)
1167{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001168 struct nc_pollsession *ps;
1169
1170 ps = calloc(1, sizeof(struct nc_pollsession));
roman3a95bb22023-10-26 11:07:17 +02001171 NC_CHECK_ERRMEM_RET(!ps, NULL);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001172 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001173 pthread_mutex_init(&ps->lock, NULL);
1174
1175 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001176}
1177
1178API void
1179nc_ps_free(struct nc_pollsession *ps)
1180{
fanchanghu3d4e7212017-08-09 09:42:30 +08001181 uint16_t i;
1182
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001183 if (!ps) {
1184 return;
1185 }
1186
Michal Vaskobe86fe32016-04-07 10:43:03 +02001187 if (ps->queue_len) {
Michal Vasko05532772021-06-03 12:12:38 +02001188 ERR(NULL, "FATAL: Freeing a pollsession structure that is currently being worked with!");
Michal Vaskobe86fe32016-04-07 10:43:03 +02001189 }
1190
fanchanghu3d4e7212017-08-09 09:42:30 +08001191 for (i = 0; i < ps->session_count; i++) {
1192 free(ps->sessions[i]);
1193 }
1194
Michal Vasko428087d2016-01-14 16:04:28 +01001195 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001196 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001197 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001198
Michal Vasko428087d2016-01-14 16:04:28 +01001199 free(ps);
1200}
1201
1202API int
1203nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1204{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001205 uint8_t q_id;
1206
romanf578cd52023-10-19 09:47:40 +02001207 NC_CHECK_ARG_RET(session, ps, session, -1);
Michal Vasko428087d2016-01-14 16:04:28 +01001208
Michal Vasko48a63ed2016-03-01 09:48:21 +01001209 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001210 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001211 return -1;
1212 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001213
Michal Vasko428087d2016-01-14 16:04:28 +01001214 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001215 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001216 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001217 ERRMEM;
1218 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001219 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001220 return -1;
1221 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001222 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1223 if (!ps->sessions[ps->session_count - 1]) {
1224 ERRMEM;
1225 --ps->session_count;
1226 /* UNLOCK */
1227 nc_ps_unlock(ps, q_id, __func__);
1228 return -1;
1229 }
1230 ps->sessions[ps->session_count - 1]->session = session;
1231 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001232
Michal Vasko48a63ed2016-03-01 09:48:21 +01001233 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001234 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001235}
1236
Michal Vasko48a63ed2016-03-01 09:48:21 +01001237static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001238_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001239{
1240 uint16_t i;
1241
Radek Krejcid5f978f2016-03-03 13:14:45 +01001242 if (index >= 0) {
1243 i = (uint16_t)index;
1244 goto remove;
1245 }
Michal Vasko428087d2016-01-14 16:04:28 +01001246 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001247 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001248remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001249 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001250 if (i <= ps->session_count) {
1251 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001252 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001253 }
1254 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001255 free(ps->sessions);
1256 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001257 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001258 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001259 return 0;
1260 }
1261 }
1262
Michal Vaskof0537d82016-01-29 14:42:38 +01001263 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001264}
1265
Michal Vasko48a63ed2016-03-01 09:48:21 +01001266API int
1267nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1268{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001269 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001270 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001271
romanf578cd52023-10-19 09:47:40 +02001272 NC_CHECK_ARG_RET(session, ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001273
1274 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001275 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001276 return -1;
1277 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001278
Radek Krejcid5f978f2016-03-03 13:14:45 +01001279 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001280
1281 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001282 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001283
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001284 return ret || ret2 ? -1 : 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001285}
1286
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001287API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001288nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001289{
1290 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001291 struct nc_session *ret = NULL;
1292
romanf578cd52023-10-19 09:47:40 +02001293 NC_CHECK_ARG_RET(NULL, ps, NULL);
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001294
1295 /* LOCK */
1296 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1297 return NULL;
1298 }
1299
Michal Vasko4871c9d2017-10-09 14:48:39 +02001300 if (idx < ps->session_count) {
1301 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001302 }
1303
1304 /* UNLOCK */
1305 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1306
1307 return ret;
1308}
1309
Michal Vasko3ec3b112022-07-21 12:32:33 +02001310API struct nc_session *
1311nc_ps_find_session(const struct nc_pollsession *ps, nc_ps_session_match_cb match_cb, void *cb_data)
1312{
1313 uint8_t q_id;
1314 uint16_t i;
1315 struct nc_session *ret = NULL;
1316
romanf578cd52023-10-19 09:47:40 +02001317 NC_CHECK_ARG_RET(NULL, ps, NULL);
Michal Vasko3ec3b112022-07-21 12:32:33 +02001318
1319 /* LOCK */
1320 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1321 return NULL;
1322 }
1323
1324 for (i = 0; i < ps->session_count; ++i) {
1325 if (match_cb(ps->sessions[i]->session, cb_data)) {
1326 ret = ps->sessions[i]->session;
1327 break;
1328 }
1329 }
1330
1331 /* UNLOCK */
1332 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1333
1334 return ret;
1335}
1336
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001337API uint16_t
1338nc_ps_session_count(struct nc_pollsession *ps)
1339{
Michal Vasko47003942019-03-14 12:25:23 +01001340 uint8_t q_id;
1341 uint16_t session_count;
1342
romanf578cd52023-10-19 09:47:40 +02001343 NC_CHECK_ARG_RET(NULL, ps, 0);
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001344
Michal Vasko47003942019-03-14 12:25:23 +01001345 /* LOCK (just for memory barrier so that we read the current value) */
1346 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1347 return 0;
1348 }
1349
1350 session_count = ps->session_count;
1351
1352 /* UNLOCK */
1353 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1354
1355 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001356}
1357
Michal Vasko77e83572022-07-21 15:31:15 +02001358static NC_MSG_TYPE
1359recv_rpc_check_msgid(struct nc_session *session, const struct lyd_node *envp)
1360{
1361 struct lyd_attr *attr;
1362
1363 assert(envp && !envp->schema);
1364
1365 /* find the message-id attribute */
1366 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1367 if (!strcmp(attr->name.name, "message-id")) {
1368 break;
1369 }
1370 }
1371
1372 if (!attr) {
1373 ERR(session, "Received an <rpc> without a message-id.");
1374 return NC_MSG_REPLY_ERR_MSGID;
1375 }
1376
1377 return NC_MSG_RPC;
1378}
1379
Michal Vasko131120a2018-05-29 15:44:02 +02001380/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001381 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001382 * NC_PSPOLL_TIMEOUT,
Michal Vaskof8fba542023-10-23 12:03:50 +02001383 * NC_PSPOLL_BAD_RPC (| NC_PSPOLL_REPLY_ERROR),
Michal Vasko71090fc2016-05-24 16:37:28 +02001384 * NC_PSPOLL_RPC
1385 */
1386static int
Michal Vasko131120a2018-05-29 15:44:02 +02001387nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001388{
Michal Vasko77367452021-02-16 16:32:18 +01001389 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001390 struct nc_server_reply *reply = NULL;
Michal Vasko939ffce2021-04-12 13:02:01 +02001391 struct lyd_node *e;
Michal Vaskof8fba542023-10-23 12:03:50 +02001392 int r, ret = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001393
romanf578cd52023-10-19 09:47:40 +02001394 NC_CHECK_ARG_RET(session, session, rpc, NC_PSPOLL_ERROR);
1395
1396 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vasko05532772021-06-03 12:12:38 +02001397 ERR(session, "Invalid session to receive RPCs.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001398 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001399 }
1400
Michal Vasko93224072021-11-09 12:14:28 +01001401 *rpc = NULL;
1402
Michal Vasko77367452021-02-16 16:32:18 +01001403 /* get a message */
1404 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1405 if (r == -2) {
1406 /* malformed message */
Michal Vasko93224072021-11-09 12:14:28 +01001407 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77e83572022-07-21 15:31:15 +02001408 goto cleanup;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001409 }
1410 if (r == -1) {
Michal Vasko77367452021-02-16 16:32:18 +01001411 return NC_PSPOLL_ERROR;
1412 } else if (!r) {
1413 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001414 }
1415
Michal Vasko77367452021-02-16 16:32:18 +01001416 *rpc = calloc(1, sizeof **rpc);
roman3a95bb22023-10-26 11:07:17 +02001417 NC_CHECK_ERRMEM_GOTO(!*rpc, ret = NC_PSPOLL_ERROR, cleanup);
Michal Vasko77367452021-02-16 16:32:18 +01001418
1419 /* parse the RPC */
Michal Vasko77e83572022-07-21 15:31:15 +02001420 if (!lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_RPC_NETCONF, &(*rpc)->envp, &(*rpc)->rpc)) {
1421 /* check message-id */
1422 if (recv_rpc_check_msgid(session, (*rpc)->envp) == NC_MSG_RPC) {
1423 /* valid RPC */
1424 ret = NC_PSPOLL_RPC;
1425 } else {
1426 /* no message-id */
Michal Vasko77e83572022-07-21 15:31:15 +02001427 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MISSING_ATTR, NC_ERR_TYPE_RPC, "message-id", "rpc"));
1428 }
1429 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001430 /* bad RPC received */
Michal Vasko77367452021-02-16 16:32:18 +01001431 if ((*rpc)->envp) {
1432 /* at least the envelopes were parsed */
Michal Vasko93224072021-11-09 12:14:28 +01001433 e = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
Michal Vasko58791da2024-02-26 13:52:59 +01001434 nc_err_set_msg(e, ly_err_last(session->ctx)->msg, "en");
Michal Vasko939ffce2021-04-12 13:02:01 +02001435 reply = nc_server_reply_err(e);
Michal Vasko77367452021-02-16 16:32:18 +01001436 } else if (session->version == NC_VERSION_11) {
Michal Vasko93224072021-11-09 12:14:28 +01001437 /* completely malformed message, NETCONF version 1.1 defines sending error reply from
1438 * the server (RFC 6241 sec. 3) */
1439 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vaskof8fba542023-10-23 12:03:50 +02001440 } else {
1441 /* at least set the return value */
1442 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko77367452021-02-16 16:32:18 +01001443 }
Michal Vasko77367452021-02-16 16:32:18 +01001444 }
1445
1446cleanup:
Michal Vasko77e83572022-07-21 15:31:15 +02001447 if (reply) {
1448 /* send error reply */
1449 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, *rpc ? (*rpc)->envp : NULL, reply);
1450 nc_server_reply_free(reply);
1451 if (r != NC_MSG_REPLY) {
1452 ERR(session, "Failed to write reply (%s), terminating session.", nc_msgtype2str[r]);
1453 if (session->status != NC_STATUS_INVALID) {
1454 session->status = NC_STATUS_INVALID;
1455 session->term_reason = NC_SESSION_TERM_OTHER;
1456 }
1457 }
Michal Vaskof8fba542023-10-23 12:03:50 +02001458
1459 /* bad RPC and an error reply sent */
1460 ret = NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR;
Michal Vasko77e83572022-07-21 15:31:15 +02001461 }
1462
Michal Vasko77367452021-02-16 16:32:18 +01001463 ly_in_free(msg, 1);
1464 if (ret != NC_PSPOLL_RPC) {
1465 nc_server_rpc_free(*rpc);
1466 *rpc = NULL;
1467 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001468 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001469}
1470
fanchanghu966f2de2016-07-21 02:28:57 -04001471API void
1472nc_set_global_rpc_clb(nc_rpc_clb clb)
1473{
1474 global_rpc_clb = clb;
1475}
1476
Radek Krejci93e80222016-10-03 13:34:25 +02001477API NC_MSG_TYPE
1478nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1479{
Michal Vasko131120a2018-05-29 15:44:02 +02001480 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001481
1482 /* check parameters */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001483 if (!session || (session->side != NC_SERVER) || !nc_session_get_notif_status(session)) {
romanf578cd52023-10-19 09:47:40 +02001484 ERRARG(NULL, "session");
Radek Krejci93e80222016-10-03 13:34:25 +02001485 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001486 } else if (!notif || !notif->ntf || !notif->eventtime) {
romanf578cd52023-10-19 09:47:40 +02001487 ERRARG(NULL, "notif");
Radek Krejci93e80222016-10-03 13:34:25 +02001488 return NC_MSG_ERROR;
1489 }
1490
Michal Vasko131120a2018-05-29 15:44:02 +02001491 /* we do not need RPC lock for this, IO lock will be acquired properly */
1492 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001493 if (ret != NC_MSG_NOTIF) {
Michal Vasko05532772021-06-03 12:12:38 +02001494 ERR(session, "Failed to write notification (%s).", nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001495 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001496
Michal Vasko131120a2018-05-29 15:44:02 +02001497 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001498}
1499
Michal Vaskof9467762023-03-28 09:02:08 +02001500/**
1501 * @brief Send a reply acquiring IO lock as needed.
1502 * Session RPC lock must be held!
1503 *
1504 * @param[in] session Session to use.
1505 * @param[in] io_timeout Timeout to use for acquiring IO lock.
1506 * @param[in] rpc RPC to sent.
1507 * @return 0 on success.
1508 * @return Bitmask of NC_PSPOLL_ERROR (any fatal error) and NC_PSPOLL_REPLY_ERROR (reply failed to be sent).
1509 * @return NC_PSPOLL_ERROR on other errors.
Michal Vasko71090fc2016-05-24 16:37:28 +02001510 */
1511static int
Michal Vasko93224072021-11-09 12:14:28 +01001512nc_server_send_reply_io(struct nc_session *session, int io_timeout, const struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001513{
1514 nc_rpc_clb clb;
1515 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001516 const struct lysc_node *rpc_act = NULL;
1517 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001518 int ret = 0;
1519 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001520
Michal Vasko4a827e52016-03-03 10:59:00 +01001521 if (!rpc) {
1522 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001523 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001524 }
1525
Michal Vasko77367452021-02-16 16:32:18 +01001526 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001527 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001528 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001529 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001530 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001531 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001532 if (elem->schema->nodetype == LYS_ACTION) {
1533 rpc_act = elem->schema;
1534 break;
1535 }
Michal Vasko77367452021-02-16 16:32:18 +01001536 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001537 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001538 if (!rpc_act) {
1539 ERRINT;
1540 return NC_PSPOLL_ERROR;
1541 }
1542 }
1543
1544 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001545 if (!global_rpc_clb) {
1546 /* no callback, reply with a not-implemented error */
Michal Vasko93224072021-11-09 12:14:28 +01001547 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 +03001548 } else {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001549 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001550 }
Michal Vasko428087d2016-01-14 16:04:28 +01001551 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001552 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001553 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001554 }
1555
1556 if (!reply) {
Michal Vasko93224072021-11-09 12:14:28 +01001557 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001558 }
Michal Vasko77367452021-02-16 16:32:18 +01001559 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001560 if (reply->type == NC_RPL_ERROR) {
1561 ret |= NC_PSPOLL_REPLY_ERROR;
1562 }
1563 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001564
Michal Vasko131120a2018-05-29 15:44:02 +02001565 if (r != NC_MSG_REPLY) {
Michal Vasko15469492021-06-09 08:40:48 +02001566 ERR(session, "Failed to write reply (%s).", nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001567 ret |= NC_PSPOLL_ERROR;
1568 }
Michal Vasko428087d2016-01-14 16:04:28 +01001569
1570 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1571 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1572 session->status = NC_STATUS_INVALID;
1573 }
1574
Michal Vasko71090fc2016-05-24 16:37:28 +02001575 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001576}
1577
Michal Vaskof9467762023-03-28 09:02:08 +02001578/**
1579 * @brief Poll a session from pspoll acquiring IO lock as needed.
1580 * Session must be running and session RPC lock held!
1581 *
1582 * @param[in] session Session to use.
1583 * @param[in] io_timeout Timeout to use for acquiring IO lock.
1584 * @param[in] now_mono Current monotonic timestamp.
1585 * @param[in,out] msg Message to fill in case of an error.
1586 * @return NC_PSPOLL_RPC if some application data are available.
1587 * @return NC_PSPOLL_TIMEOUT if a timeout elapsed.
1588 * @return NC_PSPOLL_SSH_CHANNEL if a new SSH channel has been created.
1589 * @return NC_PSPOLL_SSH_MSG if just an SSH message has been processed.
1590 * @return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR if session has been terminated (@p msg filled).
1591 * @return NC_PSPOLL_ERROR on other fatal errors (@p msg filled).
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001592 */
1593static int
Michal Vasko131120a2018-05-29 15:44:02 +02001594nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001595{
Michal Vasko9a327362017-01-11 11:31:46 +01001596 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001597 int r, ret = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001598
romanf578cd52023-10-19 09:47:40 +02001599#ifdef NC_ENABLED_SSH_TLS
roman456f92d2023-04-28 10:28:12 +02001600 ssh_message ssh_msg;
Michal Vasko9a327362017-01-11 11:31:46 +01001601 struct nc_session *new;
romanf578cd52023-10-19 09:47:40 +02001602#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko428087d2016-01-14 16:04:28 +01001603
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001604 /* check timeout first */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001605 if (!(session->flags & NC_SESSION_CALLHOME) && !nc_session_get_notif_status(session) && server_opts.idle_timeout &&
romanf578cd52023-10-19 09:47:40 +02001606 (now_mono >= session->opts.server.last_rpc + (unsigned) server_opts.idle_timeout)) {
Michal Vasko4607daf2024-01-15 15:05:15 +01001607 sprintf(msg, "Session idle timeout elapsed");
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001608 session->status = NC_STATUS_INVALID;
1609 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1610 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1611 }
1612
Michal Vasko131120a2018-05-29 15:44:02 +02001613 r = nc_session_io_lock(session, io_timeout, __func__);
1614 if (r < 0) {
Michal Vasko4607daf2024-01-15 15:05:15 +01001615 sprintf(msg, "Session IO lock failed to be acquired");
Michal Vasko131120a2018-05-29 15:44:02 +02001616 return NC_PSPOLL_ERROR;
1617 } else if (!r) {
1618 return NC_PSPOLL_TIMEOUT;
1619 }
1620
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001621 switch (session->ti_type) {
romanf578cd52023-10-19 09:47:40 +02001622#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001623 case NC_TI_LIBSSH:
romanf578cd52023-10-19 09:47:40 +02001624 ssh_msg = ssh_message_get(session->ti.libssh.session);
1625 if (ssh_msg) {
1626 nc_session_ssh_msg(session, NULL, ssh_msg, NULL);
1627 if (session->ti.libssh.next) {
1628 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1629 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
1630 (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1631 /* new NETCONF SSH channel */
1632 ret = NC_PSPOLL_SSH_CHANNEL;
1633 break;
1634 }
1635 }
1636 if (new != session) {
1637 ssh_message_free(ssh_msg);
1638 break;
1639 }
1640 }
1641 if (!ret) {
1642 /* just some SSH message */
1643 ret = NC_PSPOLL_SSH_MSG;
1644 }
1645 ssh_message_free(ssh_msg);
1646
1647 /* break because 1) we don't want to return anything here ORred with NC_PSPOLL_RPC
1648 * and 2) we don't want to delay openning a new channel by waiting for a RPC to get processed
1649 */
1650 break;
1651 }
1652
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001653 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001654 if (r == SSH_EOF) {
1655 sprintf(msg, "SSH channel unexpected EOF");
1656 session->status = NC_STATUS_INVALID;
1657 session->term_reason = NC_SESSION_TERM_DROPPED;
1658 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1659 } else if (r == SSH_ERROR) {
1660 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001661 session->status = NC_STATUS_INVALID;
1662 session->term_reason = NC_SESSION_TERM_OTHER;
1663 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001664 } else if (!r) {
romanf578cd52023-10-19 09:47:40 +02001665 /* no application data received */
1666 ret = NC_PSPOLL_TIMEOUT;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001667 } else {
1668 /* we have some application data */
1669 ret = NC_PSPOLL_RPC;
1670 }
1671 break;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001672 case NC_TI_OPENSSL:
1673 r = SSL_pending(session->ti.tls);
1674 if (!r) {
1675 /* no data pending in the SSL buffer, poll fd */
1676 pfd.fd = SSL_get_rfd(session->ti.tls);
1677 if (pfd.fd < 0) {
Michal Vasko4607daf2024-01-15 15:05:15 +01001678 sprintf(msg, "Internal error (%s:%d)", __FILE__, __LINE__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001679 ret = NC_PSPOLL_ERROR;
1680 break;
1681 }
1682 pfd.events = POLLIN;
1683 pfd.revents = 0;
1684 r = poll(&pfd, 1, 0);
1685
1686 if ((r < 0) && (errno != EINTR)) {
Michal Vasko4607daf2024-01-15 15:05:15 +01001687 sprintf(msg, "Poll failed (%s)", strerror(errno));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001688 session->status = NC_STATUS_INVALID;
1689 ret = NC_PSPOLL_ERROR;
1690 } else if (r > 0) {
1691 if (pfd.revents & (POLLHUP | POLLNVAL)) {
Michal Vasko4607daf2024-01-15 15:05:15 +01001692 sprintf(msg, "Communication socket unexpectedly closed");
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001693 session->status = NC_STATUS_INVALID;
1694 session->term_reason = NC_SESSION_TERM_DROPPED;
1695 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1696 } else if (pfd.revents & POLLERR) {
Michal Vasko4607daf2024-01-15 15:05:15 +01001697 sprintf(msg, "Communication socket error");
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001698 session->status = NC_STATUS_INVALID;
1699 session->term_reason = NC_SESSION_TERM_OTHER;
1700 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1701 } else {
1702 ret = NC_PSPOLL_RPC;
1703 }
1704 } else {
1705 ret = NC_PSPOLL_TIMEOUT;
1706 }
1707 } else {
1708 ret = NC_PSPOLL_RPC;
1709 }
1710 break;
romanf578cd52023-10-19 09:47:40 +02001711#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001712 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001713 case NC_TI_UNIX:
1714 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001715 pfd.events = POLLIN;
1716 pfd.revents = 0;
1717 r = poll(&pfd, 1, 0);
1718
1719 if ((r < 0) && (errno != EINTR)) {
Michal Vasko4607daf2024-01-15 15:05:15 +01001720 sprintf(msg, "Poll failed (%s)", strerror(errno));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001721 session->status = NC_STATUS_INVALID;
1722 ret = NC_PSPOLL_ERROR;
1723 } else if (r > 0) {
1724 if (pfd.revents & (POLLHUP | POLLNVAL)) {
Michal Vasko4607daf2024-01-15 15:05:15 +01001725 sprintf(msg, "Communication socket unexpectedly closed");
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001726 session->status = NC_STATUS_INVALID;
1727 session->term_reason = NC_SESSION_TERM_DROPPED;
1728 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1729 } else if (pfd.revents & POLLERR) {
Michal Vasko4607daf2024-01-15 15:05:15 +01001730 sprintf(msg, "Communication socket error");
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001731 session->status = NC_STATUS_INVALID;
1732 session->term_reason = NC_SESSION_TERM_OTHER;
1733 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1734 } else {
1735 ret = NC_PSPOLL_RPC;
1736 }
1737 } else {
1738 ret = NC_PSPOLL_TIMEOUT;
1739 }
1740 break;
1741 case NC_TI_NONE:
Michal Vasko4607daf2024-01-15 15:05:15 +01001742 sprintf(msg, "Internal error (%s:%d)", __FILE__, __LINE__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001743 ret = NC_PSPOLL_ERROR;
1744 break;
1745 }
1746
Michal Vasko131120a2018-05-29 15:44:02 +02001747 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001748 return ret;
1749}
1750
1751API int
1752nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1753{
Michal Vasko443faa02022-10-20 09:09:03 +02001754 int ret = NC_PSPOLL_ERROR, r;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001755 uint8_t q_id;
1756 uint16_t i, j;
1757 char msg[256];
1758 struct timespec ts_timeout, ts_cur;
1759 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001760 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001761 struct nc_server_rpc *rpc = NULL;
1762
romanf578cd52023-10-19 09:47:40 +02001763 NC_CHECK_ARG_RET(NULL, ps, NC_PSPOLL_ERROR);
Michal Vasko428087d2016-01-14 16:04:28 +01001764
Michal Vaskoade892d2017-02-22 13:40:35 +01001765 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001766 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001767 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001768 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001769
Michal Vaskoade892d2017-02-22 13:40:35 +01001770 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001771 nc_ps_unlock(ps, q_id, __func__);
1772 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001773 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001774
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001775 /* fill timespecs */
Michal Vaskod8a74192023-02-06 15:51:50 +01001776 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko36c7be82017-02-22 13:37:59 +01001777 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001778 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001779 }
1780
1781 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001782 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001783 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001784 if (ps->last_event_session == ps->session_count - 1) {
1785 i = j = 0;
1786 } else {
1787 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001788 }
Michal Vasko9a327362017-01-11 11:31:46 +01001789 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001790 cur_ps_session = ps->sessions[i];
1791 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001792
Michal Vasko131120a2018-05-29 15:44:02 +02001793 /* SESSION RPC LOCK */
1794 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001795 if (r == -1) {
1796 ret = NC_PSPOLL_ERROR;
1797 } else if (r == 1) {
1798 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001799 switch (cur_ps_session->state) {
1800 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001801 if (cur_session->status == NC_STATUS_RUNNING) {
1802 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001803 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001804
Michal Vasko131120a2018-05-29 15:44:02 +02001805 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001806 switch (ret) {
1807 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001808 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001809 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001810 break;
1811 case NC_PSPOLL_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001812 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001813 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001814 break;
1815 case NC_PSPOLL_TIMEOUT:
romanf578cd52023-10-19 09:47:40 +02001816#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001817 case NC_PSPOLL_SSH_CHANNEL:
1818 case NC_PSPOLL_SSH_MSG:
romanf578cd52023-10-19 09:47:40 +02001819#endif /* NC_ENABLED_SSH_TLS */
fanchanghu3d4e7212017-08-09 09:42:30 +08001820 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001821 break;
1822 case NC_PSPOLL_RPC:
1823 /* let's keep the state busy, we are not done with this session */
1824 break;
1825 }
1826 } else {
1827 /* session is not fine, let the caller know */
1828 ret = NC_PSPOLL_SESSION_TERM;
1829 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1830 ret |= NC_PSPOLL_SESSION_ERROR;
1831 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001832 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001833 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001834 break;
1835 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001836 /* it definitely should not be busy because we have the lock */
1837 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001838 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001839 break;
1840 case NC_PS_STATE_INVALID:
1841 /* we got it locked, but it will be freed, let it be */
1842 ret = NC_PSPOLL_TIMEOUT;
1843 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001844 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001845
Michal Vasko131120a2018-05-29 15:44:02 +02001846 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001847 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001848 /* SESSION RPC UNLOCK */
1849 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001850 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001851 } else {
1852 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001853 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001854 }
Michal Vasko428087d2016-01-14 16:04:28 +01001855
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001856 /* something happened */
1857 if (ret != NC_PSPOLL_TIMEOUT) {
1858 break;
1859 }
1860
Michal Vasko9a327362017-01-11 11:31:46 +01001861 if (i == ps->session_count - 1) {
1862 i = 0;
1863 } else {
1864 ++i;
1865 }
1866 } while (i != j);
1867
Michal Vaskoade892d2017-02-22 13:40:35 +01001868 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001869 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001870 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001871
Michal Vaskod8a74192023-02-06 15:51:50 +01001872 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001873 /* final timeout */
1874 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001875 }
Michal Vasko428087d2016-01-14 16:04:28 +01001876 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001877 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001878
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001879 /* do we want to return the session? */
1880 switch (ret) {
1881 case NC_PSPOLL_RPC:
1882 case NC_PSPOLL_SESSION_TERM:
1883 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
romanf578cd52023-10-19 09:47:40 +02001884#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001885 case NC_PSPOLL_SSH_CHANNEL:
1886 case NC_PSPOLL_SSH_MSG:
romanf578cd52023-10-19 09:47:40 +02001887#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001888 if (session) {
1889 *session = cur_session;
1890 }
1891 ps->last_event_session = i;
1892 break;
1893 default:
1894 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001895 }
Michal Vasko428087d2016-01-14 16:04:28 +01001896
Michal Vaskoade892d2017-02-22 13:40:35 +01001897 /* PS UNLOCK */
1898 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001899
Michal Vasko131120a2018-05-29 15:44:02 +02001900 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001901 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001902 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001903 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1904 if (cur_session->status != NC_STATUS_RUNNING) {
1905 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001906 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001907 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001908 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001909 }
1910 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001911 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001912
Michal Vasko7f1ee932018-10-11 09:41:42 +02001913 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001914 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001915 if (cur_session->status != NC_STATUS_RUNNING) {
1916 ret |= NC_PSPOLL_SESSION_TERM;
1917 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1918 ret |= NC_PSPOLL_SESSION_ERROR;
1919 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001920 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001921 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001922 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001923 }
Michal Vasko428087d2016-01-14 16:04:28 +01001924 }
Michal Vasko77367452021-02-16 16:32:18 +01001925 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001926
Michal Vasko131120a2018-05-29 15:44:02 +02001927 /* SESSION RPC UNLOCK */
1928 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001929 }
1930
Michal Vasko48a63ed2016-03-01 09:48:21 +01001931 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001932}
1933
Michal Vaskod09eae62016-02-01 10:32:52 +01001934API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001935nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001936{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001937 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001938 uint16_t i;
1939 struct nc_session *session;
1940
Michal Vasko9a25e932016-02-01 10:36:42 +01001941 if (!ps) {
romanf578cd52023-10-19 09:47:40 +02001942 ERRARG(NULL, "ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001943 return;
1944 }
1945
Michal Vasko48a63ed2016-03-01 09:48:21 +01001946 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001947 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001948 return;
1949 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001950
Michal Vasko48a63ed2016-03-01 09:48:21 +01001951 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001952 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001953 nc_session_free(ps->sessions[i]->session, data_free);
1954 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001955 }
1956 free(ps->sessions);
1957 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001958 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001959 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001960 } else {
1961 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001962 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1963 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001964 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001965 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001966 continue;
1967 }
1968
1969 ++i;
1970 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001971 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001972
1973 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001974 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001975}
1976
romanfb3f7cf2023-11-30 16:10:09 +01001977int
1978nc_server_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port)
1979{
1980 int sock = -1, set_addr, ret = 0;
1981
1982 assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX));
1983
1984 if (address) {
1985 set_addr = 1;
1986 } else {
1987 set_addr = 0;
1988 }
1989
1990 if (set_addr) {
1991 port = bind->port;
1992 } else {
1993 address = bind->address;
1994 }
1995
1996 /* we have all the information we need to create a listening socket */
1997 if ((address && port) || (endpt->ti == NC_TI_UNIX)) {
1998 /* create new socket, close the old one */
1999 if (endpt->ti == NC_TI_UNIX) {
2000 sock = nc_sock_listen_unix(endpt->opts.unixsock);
2001 } else {
2002 sock = nc_sock_listen_inet(address, port, &endpt->ka);
2003 }
2004
2005 if (sock == -1) {
2006 ret = 1;
2007 goto cleanup;
2008 }
2009
2010 if (bind->sock > -1) {
2011 close(bind->sock);
2012 }
2013 bind->sock = sock;
2014 }
2015
2016 if (sock > -1) {
2017 switch (endpt->ti) {
2018 case NC_TI_UNIX:
2019 VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address);
2020 break;
2021#ifdef NC_ENABLED_SSH_TLS
2022 case NC_TI_LIBSSH:
2023 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
2024 break;
2025 case NC_TI_OPENSSL:
2026 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
2027 break;
2028#endif /* NC_ENABLED_SSH_TLS */
2029 default:
2030 ERRINT;
2031 ret = 1;
2032 break;
2033 }
2034 }
2035
2036cleanup:
2037 return ret;
2038}
2039
Michal Vasko29f2f022024-03-13 09:06:48 +01002040#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
2041
Michal Vasko6f865982023-11-21 12:10:42 +01002042/**
2043 * @brief Get UID of the owner of a socket.
2044 *
2045 * @param[in] sock Socket to analyze.
2046 * @param[out] uid Socket owner UID.
2047 * @return 0 on success,
2048 * @return -1 on error.
2049 */
Michal Vasko5f352c52019-07-10 16:12:06 +02002050static int
apropp-molex4e903c32020-04-20 03:06:58 -04002051nc_get_uid(int sock, uid_t *uid)
2052{
Michal Vasko6f865982023-11-21 12:10:42 +01002053 int r;
apropp-molex4e903c32020-04-20 03:06:58 -04002054
Michal Vaskod3910912020-04-20 09:12:49 +02002055#ifdef SO_PEERCRED
2056 struct ucred ucred;
2057 socklen_t len;
Michal Vasko292c5542023-02-01 14:33:17 +01002058
Michal Vaskod3910912020-04-20 09:12:49 +02002059 len = sizeof(ucred);
Michal Vasko6f865982023-11-21 12:10:42 +01002060 r = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
2061 if (!r) {
Michal Vaskod3910912020-04-20 09:12:49 +02002062 *uid = ucred.uid;
2063 }
2064#else
Michal Vasko6f865982023-11-21 12:10:42 +01002065 r = getpeereid(sock, uid, NULL);
Michal Vaskod3910912020-04-20 09:12:49 +02002066#endif
2067
Michal Vasko6f865982023-11-21 12:10:42 +01002068 if (r < 0) {
2069 ERR(NULL, "Failed to get owner UID of a UNIX socket (%s).", strerror(errno));
Michal Vaskod3910912020-04-20 09:12:49 +02002070 return -1;
2071 }
apropp-molex4e903c32020-04-20 03:06:58 -04002072 return 0;
2073}
2074
Michal Vasko29f2f022024-03-13 09:06:48 +01002075#endif
2076
apropp-molex4e903c32020-04-20 03:06:58 -04002077static int
Michal Vasko5f352c52019-07-10 16:12:06 +02002078nc_accept_unix(struct nc_session *session, int sock)
2079{
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002080#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02002081 struct passwd *pw, pw_buf;
Michal Vasko5f352c52019-07-10 16:12:06 +02002082 char *username;
Michal Vasko292c5542023-02-01 14:33:17 +01002083
Michal Vasko5f352c52019-07-10 16:12:06 +02002084 session->ti_type = NC_TI_UNIX;
Michal Vasko143aa142021-10-01 15:31:48 +02002085 uid_t uid = 0;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02002086 char *buf = NULL;
2087 size_t buf_len = 0;
Michal Vasko5f352c52019-07-10 16:12:06 +02002088
Michal Vaskod3910912020-04-20 09:12:49 +02002089 if (nc_get_uid(sock, &uid)) {
2090 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02002091 return -1;
2092 }
2093
romanf6e32012023-04-24 15:51:26 +02002094 pw = nc_getpw(uid, NULL, &pw_buf, &buf, &buf_len);
Michal Vasko5f352c52019-07-10 16:12:06 +02002095 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02002096 ERR(NULL, "Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02002097 close(sock);
2098 return -1;
2099 }
2100
2101 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02002102 free(buf);
Michal Vasko5f352c52019-07-10 16:12:06 +02002103 if (username == NULL) {
2104 ERRMEM;
2105 close(sock);
2106 return -1;
2107 }
Michal Vasko93224072021-11-09 12:14:28 +01002108 session->username = username;
Michal Vasko5f352c52019-07-10 16:12:06 +02002109
2110 session->ti.unixsock.sock = sock;
2111
2112 return 1;
Claus Klein22091912020-01-20 13:45:47 +01002113#else
Michal Vasko29f2f022024-03-13 09:06:48 +01002114 (void)session;
2115 (void)sock;
2116
Claus Klein22091912020-01-20 13:45:47 +01002117 return -1;
2118#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02002119}
2120
Michal Vaskoe2713da2016-08-22 16:06:40 +02002121API int
romanfb3f7cf2023-11-30 16:10:09 +01002122nc_server_add_endpt_unix_socket_listen(const char *endpt_name, const char *unix_socket_path, mode_t mode, uid_t uid, gid_t gid)
2123{
2124 int ret = 0;
2125 void *tmp;
2126 uint16_t i;
2127
2128 NC_CHECK_ARG_RET(NULL, endpt_name, unix_socket_path, 1);
2129
2130 /* CONFIG LOCK */
2131 pthread_rwlock_wrlock(&server_opts.config_lock);
2132
2133 /* check name uniqueness */
2134 for (i = 0; i < server_opts.endpt_count; i++) {
2135 if (!strcmp(endpt_name, server_opts.endpts[i].name)) {
2136 ERR(NULL, "Endpoint \"%s\" already exists.", endpt_name);
2137 ret = 1;
2138 goto cleanup;
2139 }
2140 }
2141
2142 /* alloc a new endpoint */
2143 tmp = nc_realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
2144 NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup);
2145 server_opts.endpts = tmp;
2146 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
2147
2148 /* alloc a new bind */
2149 tmp = nc_realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds);
2150 NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup);
2151 server_opts.binds = tmp;
2152 memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds);
2153 server_opts.binds[server_opts.endpt_count].sock = -1;
2154 server_opts.endpt_count++;
2155
2156 /* set name and ti */
2157 server_opts.endpts[server_opts.endpt_count - 1].name = strdup(endpt_name);
2158 NC_CHECK_ERRMEM_GOTO(!server_opts.endpts[server_opts.endpt_count - 1].name, ret = 1, cleanup);
2159 server_opts.endpts[server_opts.endpt_count - 1].ti = NC_TI_UNIX;
2160
2161 /* set the bind data */
2162 server_opts.binds[server_opts.endpt_count - 1].address = strdup(unix_socket_path);
2163 NC_CHECK_ERRMEM_GOTO(!server_opts.binds[server_opts.endpt_count - 1].address, ret = 1, cleanup);
2164
2165 /* alloc unix opts */
2166 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
2167 NC_CHECK_ERRMEM_GOTO(!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock, ret = 1, cleanup);
2168
2169 /* set the opts data */
2170 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->address = strdup(unix_socket_path);
2171 NC_CHECK_ERRMEM_GOTO(!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->address, ret = 1, cleanup);
2172 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode == (mode_t) -1) ? (mode_t) -1 : mode;
2173 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid == (uid_t) -1) ? (uid_t) -1 : uid;
2174 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid == (gid_t) -1) ? (gid_t) -1 : gid;
2175
2176 /* start listening */
2177 ret = nc_server_set_address_port(&server_opts.endpts[server_opts.endpt_count - 1],
2178 &server_opts.binds[server_opts.endpt_count - 1], NULL, 0);
2179 if (ret) {
2180 ERR(NULL, "Listening on UNIX socket \"%s\" failed.", unix_socket_path);
2181 goto cleanup;
2182 }
2183
2184cleanup:
2185 /* CONFIG UNLOCK */
2186 pthread_rwlock_unlock(&server_opts.config_lock);
2187 return ret;
2188}
2189
2190static void
2191nc_server_del_endpt_unix_socket_opts(struct nc_bind *bind, struct nc_server_unix_opts *opts)
2192{
2193 if (bind->sock > -1) {
2194 close(bind->sock);
2195 }
2196
2197 unlink(bind->address);
2198 free(bind->address);
2199 free(opts->address);
2200
2201 free(opts);
2202}
2203
2204void
2205_nc_server_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind)
2206{
2207 free(endpt->name);
2208 nc_server_del_endpt_unix_socket_opts(bind, endpt->opts.unixsock);
2209
2210 server_opts.endpt_count--;
2211 if (!server_opts.endpt_count) {
2212 free(server_opts.endpts);
2213 free(server_opts.binds);
2214 server_opts.endpts = NULL;
2215 server_opts.binds = NULL;
2216 } else if (endpt != &server_opts.endpts[server_opts.endpt_count]) {
2217 memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
2218 memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2219 }
2220}
2221
2222API void
2223nc_server_del_endpt_unix_socket(const char *endpt_name)
2224{
2225 uint16_t i;
2226 struct nc_endpt *endpt = NULL;
2227 struct nc_bind *bind;
2228
romanb6ad37a2023-12-07 13:08:46 +01002229 NC_CHECK_ARG_RET(NULL, endpt_name, );
2230
romanfb3f7cf2023-11-30 16:10:09 +01002231 /* CONFIG LOCK */
2232 pthread_rwlock_wrlock(&server_opts.config_lock);
2233
romanfb3f7cf2023-11-30 16:10:09 +01002234 for (i = 0; i < server_opts.endpt_count; i++) {
2235 if (!strcmp(server_opts.endpts[i].name, endpt_name)) {
2236 endpt = &server_opts.endpts[i];
2237 bind = &server_opts.binds[i];
2238 break;
2239 }
2240 }
2241 if (!endpt) {
2242 ERR(NULL, "Endpoint \"%s\" not found.", endpt_name);
2243 goto end;
2244 }
2245 if (endpt->ti != NC_TI_UNIX) {
2246 ERR(NULL, "Endpoint \"%s\" is not a UNIX socket endpoint.", endpt_name);
2247 goto end;
2248 }
2249
2250 _nc_server_del_endpt_unix_socket(endpt, bind);
2251
2252end:
2253 /* CONFIG UNLOCK */
2254 pthread_rwlock_unlock(&server_opts.config_lock);
2255}
2256
2257API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002258nc_server_endpt_count(void)
2259{
2260 return server_opts.endpt_count;
2261}
2262
Michal Vasko71090fc2016-05-24 16:37:28 +02002263API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +01002264nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002265{
Michal Vasko71090fc2016-05-24 16:37:28 +02002266 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002267 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002268 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002269 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002270 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002271
romanf578cd52023-10-19 09:47:40 +02002272 NC_CHECK_ARG_RET(NULL, ctx, session, NC_MSG_ERROR);
Michal Vasko9e036d52016-01-08 10:49:26 +01002273
romand82caf12024-03-05 14:21:39 +01002274 NC_CHECK_SRV_INIT_RET(NC_MSG_ERROR);
2275
2276 *session = NULL;
2277
Michal Vasko93224072021-11-09 12:14:28 +01002278 /* init ctx as needed */
romanf578cd52023-10-19 09:47:40 +02002279 nc_server_init_cb_ctx(ctx);
Michal Vasko93224072021-11-09 12:14:28 +01002280
romanf578cd52023-10-19 09:47:40 +02002281 /* CONFIG LOCK */
2282 pthread_rwlock_rdlock(&server_opts.config_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002283
2284 if (!server_opts.endpt_count) {
Michal Vasko05532772021-06-03 12:12:38 +02002285 ERR(NULL, "No endpoints to accept sessions on.");
romanf578cd52023-10-19 09:47:40 +02002286 /* CONFIG UNLOCK */
2287 pthread_rwlock_unlock(&server_opts.config_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002288 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002289 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002290
romanf578cd52023-10-19 09:47:40 +02002291 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, &server_opts.bind_lock, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002292 if (ret < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01002293 free(host);
romanf578cd52023-10-19 09:47:40 +02002294 /* CONFIG UNLOCK */
2295 pthread_rwlock_unlock(&server_opts.config_lock);
Michal Vasko5e203472016-05-30 15:27:58 +02002296 if (!ret) {
2297 return NC_MSG_WOULDBLOCK;
2298 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002299 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002300 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002301
Michal Vaskob48aa812016-01-18 14:13:09 +01002302 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002303
Michal Vasko131120a2018-05-29 15:44:02 +02002304 *session = nc_new_session(NC_SERVER, 0);
roman3a95bb22023-10-26 11:07:17 +02002305 NC_CHECK_ERRMEM_GOTO(!(*session), close(sock); free(host); msgtype = NC_MSG_ERROR, cleanup);
Michal Vasko1a38c862016-01-15 15:50:07 +01002306 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01002307 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko1a38c862016-01-15 15:50:07 +01002308 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01002309 (*session)->host = host;
Michal Vasko1a38c862016-01-15 15:50:07 +01002310 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002311
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002312 /* sock gets assigned to session or closed */
romanf578cd52023-10-19 09:47:40 +02002313#ifdef NC_ENABLED_SSH_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002314 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
romanf578cd52023-10-19 09:47:40 +02002315 ret = nc_accept_ssh_session(*session, server_opts.endpts[bind_idx].opts.ssh, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002316 if (ret < 0) {
2317 msgtype = NC_MSG_ERROR;
2318 goto cleanup;
2319 } else if (!ret) {
2320 msgtype = NC_MSG_WOULDBLOCK;
2321 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002322 }
romanf578cd52023-10-19 09:47:40 +02002323 } else if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002324 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
romanf578cd52023-10-19 09:47:40 +02002325 ret = nc_accept_tls_session(*session, server_opts.endpts[bind_idx].opts.tls, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002326 if (ret < 0) {
2327 msgtype = NC_MSG_ERROR;
2328 goto cleanup;
2329 } else if (!ret) {
2330 msgtype = NC_MSG_WOULDBLOCK;
2331 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002332 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002333 } else
romanf578cd52023-10-19 09:47:40 +02002334#endif /* NC_ENABLED_SSH_TLS */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002335 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2336 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2337 ret = nc_accept_unix(*session, sock);
2338 if (ret < 0) {
2339 msgtype = NC_MSG_ERROR;
2340 goto cleanup;
2341 }
2342 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002343 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002344 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002345 msgtype = NC_MSG_ERROR;
2346 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002347 }
2348
Michal Vasko2cc4c682016-03-01 09:16:48 +01002349 (*session)->data = NULL;
2350
romanf578cd52023-10-19 09:47:40 +02002351 /* CONFIG UNLOCK */
2352 pthread_rwlock_unlock(&server_opts.config_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002353
Michal Vaskob48aa812016-01-18 14:13:09 +01002354 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002355 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002356
Michal Vasko9e036d52016-01-08 10:49:26 +01002357 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002358 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002359 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002360 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002361 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002362 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002363 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002364
Michal Vaskod8a74192023-02-06 15:51:50 +01002365 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02002366 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +01002367 nc_realtime_get(&ts_cur);
roman44efa322023-11-03 13:57:25 +01002368 (*session)->opts.server.session_start = ts_cur;
Michal Vasko1a38c862016-01-15 15:50:07 +01002369 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002370
Michal Vasko71090fc2016-05-24 16:37:28 +02002371 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002372
Michal Vasko71090fc2016-05-24 16:37:28 +02002373cleanup:
romanf578cd52023-10-19 09:47:40 +02002374 /* CONFIG UNLOCK */
2375 pthread_rwlock_unlock(&server_opts.config_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002376
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002377 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002378 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002379 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002380}
2381
romanf578cd52023-10-19 09:47:40 +02002382#ifdef NC_ENABLED_SSH_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002383
2384API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002385nc_server_ch_is_client(const char *name)
2386{
2387 uint16_t i;
2388 int found = 0;
2389
2390 if (!name) {
2391 return found;
2392 }
2393
2394 /* READ LOCK */
2395 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2396
2397 /* check name uniqueness */
2398 for (i = 0; i < server_opts.ch_client_count; ++i) {
2399 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2400 found = 1;
2401 break;
2402 }
2403 }
2404
2405 /* UNLOCK */
2406 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2407
2408 return found;
2409}
2410
2411API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002412nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2413{
2414 uint16_t i;
2415 struct nc_ch_client *client = NULL;
2416 int found = 0;
2417
2418 if (!client_name || !endpt_name) {
2419 return found;
2420 }
2421
2422 /* READ LOCK */
2423 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2424
2425 for (i = 0; i < server_opts.ch_client_count; ++i) {
2426 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2427 client = &server_opts.ch_clients[i];
2428 break;
2429 }
2430 }
2431
2432 if (!client) {
2433 goto cleanup;
2434 }
2435
2436 for (i = 0; i < client->ch_endpt_count; ++i) {
2437 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2438 found = 1;
2439 break;
2440 }
2441 }
2442
2443cleanup:
2444 /* UNLOCK */
2445 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2446 return found;
2447}
2448
Michal Vasko056f53c2022-10-21 13:38:15 +02002449/**
2450 * @brief Create a connection for an endpoint.
2451 *
2452 * Client lock is expected to be held.
2453 *
2454 * @param[in] endpt Endpoint to use.
2455 * @param[in] acquire_ctx_cb Callback for acquiring the libyang context.
2456 * @param[in] release_ctx_cb Callback for releasing the libyang context.
2457 * @param[in] ctx_cb_data Context callbacks data.
2458 * @param[out] session Created NC session.
2459 * @return NC_MSG values.
2460 */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002461static NC_MSG_TYPE
Michal Vasko58bac1c2022-03-24 15:25:26 +01002462nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
2463 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 +01002464{
Michal Vasko71090fc2016-05-24 16:37:28 +02002465 NC_MSG_TYPE msgtype;
Michal Vasko58bac1c2022-03-24 15:25:26 +01002466 const struct ly_ctx *ctx = NULL;
Michal Vaskob05053d2016-01-22 16:12:06 +01002467 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002468 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01002469 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01002470
Michal Vasko056f53c2022-10-21 13:38:15 +02002471 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 +01002472 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002473 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002474 }
2475
Michal Vasko93224072021-11-09 12:14:28 +01002476 /* acquire context */
2477 ctx = acquire_ctx_cb(ctx_cb_data);
2478 if (!ctx) {
2479 ERR(NULL, "Failed to acquire context for a new Call Home session.");
2480 close(sock);
2481 free(ip_host);
2482 return NC_MSG_ERROR;
2483 }
2484
romanf578cd52023-10-19 09:47:40 +02002485 /* init ctx as needed */
2486 nc_server_init_cb_ctx(ctx);
2487
Michal Vasko93224072021-11-09 12:14:28 +01002488 /* create session */
Michal Vasko131120a2018-05-29 15:44:02 +02002489 *session = nc_new_session(NC_SERVER, 0);
roman3a95bb22023-10-26 11:07:17 +02002490 NC_CHECK_ERRMEM_GOTO(!(*session), close(sock); free(ip_host); msgtype = NC_MSG_ERROR, fail);
Michal Vaskob05053d2016-01-22 16:12:06 +01002491 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01002492 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vaskodc96bb92023-03-28 08:52:48 +02002493 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vasko93224072021-11-09 12:14:28 +01002494 (*session)->host = ip_host;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002495 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002496
Michal Vaskob05053d2016-01-22 16:12:06 +01002497 /* sock gets assigned to session or closed */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002498 if (endpt->ti == NC_TI_LIBSSH) {
romanf578cd52023-10-19 09:47:40 +02002499 ret = nc_accept_ssh_session(*session, endpt->opts.ssh, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002500 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002501
Michal Vasko71090fc2016-05-24 16:37:28 +02002502 if (ret < 0) {
2503 msgtype = NC_MSG_ERROR;
2504 goto fail;
2505 } else if (!ret) {
2506 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002507 goto fail;
2508 }
romanf578cd52023-10-19 09:47:40 +02002509 } else if (endpt->ti == NC_TI_OPENSSL) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002510 (*session)->data = endpt->opts.tls;
romanf578cd52023-10-19 09:47:40 +02002511 ret = nc_accept_tls_session(*session, endpt->opts.tls, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002512 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002513
Michal Vasko71090fc2016-05-24 16:37:28 +02002514 if (ret < 0) {
2515 msgtype = NC_MSG_ERROR;
2516 goto fail;
2517 } else if (!ret) {
2518 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002519 goto fail;
2520 }
roman423cc0d2023-11-24 11:29:47 +01002521 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +01002522 ERRINT;
2523 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002524 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002525 goto fail;
2526 }
2527
2528 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002529 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01002530
2531 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002532 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002533 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002534 goto fail;
2535 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002536
Michal Vaskod8a74192023-02-06 15:51:50 +01002537 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02002538 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +01002539 nc_realtime_get(&ts_cur);
roman44efa322023-11-03 13:57:25 +01002540 (*session)->opts.server.session_start = ts_cur;
Michal Vaskob05053d2016-01-22 16:12:06 +01002541 (*session)->status = NC_STATUS_RUNNING;
2542
Michal Vasko71090fc2016-05-24 16:37:28 +02002543 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002544
2545fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002546 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002547 *session = NULL;
Michal Vasko58bac1c2022-03-24 15:25:26 +01002548 if (ctx) {
2549 release_ctx_cb(ctx_cb_data);
2550 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002551 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002552}
2553
Michal Vasko6f865982023-11-21 12:10:42 +01002554/**
2555 * @brief Wait for any event after a NC session was established on a CH client.
2556 *
Michal Vasko6f865982023-11-21 12:10:42 +01002557 * @param[in] data CH client thread argument.
roman8341e8b2023-11-23 16:12:42 +01002558 * @param[in] session New NC session. The session is invalid upon being freed (= function exit).
Michal Vasko6f865982023-11-21 12:10:42 +01002559 * @return 0 if session was terminated normally,
2560 * @return 1 if the CH client was removed,
2561 * @return -1 on error.
2562 */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002563static int
roman8341e8b2023-11-23 16:12:42 +01002564nc_server_ch_client_thread_session_cond_wait(struct nc_ch_client_thread_arg *data, struct nc_session *session)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002565{
Michal Vasko6f865982023-11-21 12:10:42 +01002566 int rc = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002567 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002568 struct timespec ts;
2569 struct nc_ch_client *client;
2570
Michal Vasko2e6defd2016-10-07 15:48:15 +02002571 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01002572 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002573
Michal Vaskofeccb312022-03-24 15:24:59 +01002574 session->flags |= NC_SESSION_CH_THREAD;
Michal Vasko0db3db52021-03-03 10:45:42 +01002575
Michal Vasko2e6defd2016-10-07 15:48:15 +02002576 /* give the session to the user */
romanf578cd52023-10-19 09:47:40 +02002577 if (data->new_session_cb(data->client_name, session, data->new_session_cb_data)) {
Michal Vaskof1c26c22021-04-12 16:34:33 +02002578 /* something is wrong, free the session */
Michal Vaskofeccb312022-03-24 15:24:59 +01002579 session->flags &= ~NC_SESSION_CH_THREAD;
Michal Vaskof1c26c22021-04-12 16:34:33 +02002580
2581 /* CH UNLOCK */
2582 pthread_mutex_unlock(&session->opts.server.ch_lock);
2583
Michal Vasko77d56d72022-09-07 10:30:48 +02002584 /* session terminated, free it and release its context */
Michal Vaskof1c26c22021-04-12 16:34:33 +02002585 nc_session_free(session, NULL);
Michal Vasko58bac1c2022-03-24 15:25:26 +01002586 data->release_ctx_cb(data->ctx_cb_data);
Michal Vasko6f865982023-11-21 12:10:42 +01002587 return 0;
Michal Vaskof1c26c22021-04-12 16:34:33 +02002588 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002589
2590 do {
romanf578cd52023-10-19 09:47:40 +02002591 nc_timeouttime_get(&ts, NC_CH_THREAD_IDLE_TIMEOUT_SLEEP);
Michal Vasko6f865982023-11-21 12:10:42 +01002592
Michal Vasko0db3db52021-03-03 10:45:42 +01002593 /* CH COND WAIT */
Michal Vaskod8a74192023-02-06 15:51:50 +01002594 r = pthread_cond_clockwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, COMPAT_CLOCK_ID, &ts);
Michal Vasko3f05a092018-03-13 10:39:49 +01002595 if (!r) {
2596 /* we were woken up, something probably happened */
2597 if (session->status != NC_STATUS_RUNNING) {
2598 break;
2599 }
2600 } else if (r != ETIMEDOUT) {
Michal Vasko05532772021-06-03 12:12:38 +02002601 ERR(session, "Pthread condition timedwait failed (%s).", strerror(r));
Michal Vasko6f865982023-11-21 12:10:42 +01002602 rc = -1;
Michal Vasko3f05a092018-03-13 10:39:49 +01002603 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01002604 }
2605
Michal Vasko2e6defd2016-10-07 15:48:15 +02002606 /* check whether the client was not removed */
Michal Vasko6f865982023-11-21 12:10:42 +01002607
Michal Vasko2e6defd2016-10-07 15:48:15 +02002608 /* LOCK */
Michal Vasko6f865982023-11-21 12:10:42 +01002609 client = nc_server_ch_client_lock(data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002610 if (!client) {
2611 /* client was removed, finish thread */
Michal Vasko05532772021-06-03 12:12:38 +02002612 VRB(session, "Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02002613 data->client_name);
Michal Vasko6f865982023-11-21 12:10:42 +01002614 rc = 1;
Michal Vasko3f05a092018-03-13 10:39:49 +01002615 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002616 }
2617
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002618 if (client->conn_type == NC_CH_PERIOD) {
romanf578cd52023-10-19 09:47:40 +02002619 idle_timeout = client->idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002620 } else {
2621 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002622 }
2623
Michal Vaskod8a74192023-02-06 15:51:50 +01002624 nc_timeouttime_get(&ts, 0);
Michal Vaskodf68e7e2022-04-21 11:04:00 +02002625 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 +02002626 VRB(session, "Call Home client \"%s\": session idle timeout elapsed.", client->name);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002627 session->status = NC_STATUS_INVALID;
2628 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2629 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002630
2631 /* UNLOCK */
2632 nc_server_ch_client_unlock(client);
2633
2634 } while (session->status == NC_STATUS_RUNNING);
2635
Michal Vaskofeccb312022-03-24 15:24:59 +01002636 /* signal to nc_session_free() that CH thread is terminating */
2637 session->flags &= ~NC_SESSION_CH_THREAD;
2638 pthread_cond_signal(&session->opts.server.ch_cond);
Michal Vasko0db3db52021-03-03 10:45:42 +01002639
Michal Vasko27377422018-03-15 08:59:35 +01002640 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01002641 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01002642
Michal Vasko6f865982023-11-21 12:10:42 +01002643 return rc;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002644}
2645
romanf578cd52023-10-19 09:47:40 +02002646/**
2647 * @brief Waits for some amount of time while reacting to signals about terminating a Call Home thread.
2648 *
2649 * @param[in] session An established session.
2650 * @param[in] data Call Home thread's data.
2651 * @param[in] cond_wait_time Time in seconds to sleep for, after which a reconnect is attempted.
2652 *
2653 * @return 0 if the thread should stop running, 1 if it should continue.
2654 */
2655static int
2656nc_server_ch_client_thread_is_running_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data, uint64_t cond_wait_time)
2657{
2658 struct timespec ts;
2659 int ret = 0, thread_running;
2660
2661 /* COND LOCK */
2662 pthread_mutex_lock(&data->cond_lock);
2663 /* get reconnect timeout in ms */
2664 nc_timeouttime_get(&ts, cond_wait_time * 1000);
2665 while (!ret && data->thread_running) {
2666 ret = pthread_cond_clockwait(&data->cond, &data->cond_lock, COMPAT_CLOCK_ID, &ts);
2667 }
2668
2669 thread_running = data->thread_running;
2670 /* COND UNLOCK */
2671 pthread_mutex_unlock(&data->cond_lock);
2672
2673 if (!thread_running) {
2674 /* thread is terminating */
2675 VRB(session, "Call Home thread signaled to exit, client \"%s\" probably removed.", data->client_name);
2676 ret = 0;
2677 } else if (ret == ETIMEDOUT) {
2678 /* time to reconnect */
2679 VRB(session, "Call Home client \"%s\" timeout of %" PRIu64 " seconds expired, reconnecting.", data->client_name, cond_wait_time);
2680 ret = 1;
2681 } else if (ret) {
2682 ERR(session, "Pthread condition timedwait failed (%s).", strerror(ret));
2683 ret = 0;
2684 }
2685
2686 return ret;
2687}
2688
2689/**
2690 * @brief Checks if a Call Home thread should terminate.
2691 *
2692 * Checks the shared boolean variable thread_running. This should be done everytime
2693 * before entering a critical section.
2694 *
2695 * @param[in] data Call Home thread's data.
2696 *
2697 * @return 0 if the thread should stop running, -1 if it can continue.
2698 */
2699static int
2700nc_server_ch_client_thread_is_running(struct nc_ch_client_thread_arg *data)
2701{
2702 int ret = -1;
2703
2704 /* COND LOCK */
2705 pthread_mutex_lock(&data->cond_lock);
2706 if (!data->thread_running) {
2707 /* thread should stop running */
2708 ret = 0;
2709 }
2710 /* COND UNLOCK */
2711 pthread_mutex_unlock(&data->cond_lock);
2712
2713 return ret;
2714}
2715
Michal Vasko6f865982023-11-21 12:10:42 +01002716/**
2717 * @brief Lock CH client structures for reading and lock the specific client if it has some endpoints, wait otherwise.
2718 *
2719 * @param[in] name Name of the CH client.
2720 * @return Pointer to the CH client.
2721 */
2722static struct nc_ch_client *
2723nc_server_ch_client_with_endpt_lock(const char *name)
2724{
2725 struct nc_ch_client *client;
2726
2727 while (1) {
2728 /* LOCK */
2729 client = nc_server_ch_client_lock(name);
2730 if (!client) {
2731 return NULL;
2732 }
2733 if (client->ch_endpt_count) {
2734 return client;
2735 }
2736 /* no endpoints defined yet */
2737
2738 /* UNLOCK */
2739 nc_server_ch_client_unlock(client);
2740
2741 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2742 }
2743
2744 return NULL;
2745}
2746
2747/**
2748 * @brief Call Home client management thread.
2749 *
2750 * @param[in] arg CH client thread argument.
2751 * @return NULL.
2752 */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002753static void *
2754nc_ch_client_thread(void *arg)
2755{
Michal Vasko6f865982023-11-21 12:10:42 +01002756 struct nc_ch_client_thread_arg *data = arg;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002757 NC_MSG_TYPE msgtype;
2758 uint8_t cur_attempts = 0;
romanf578cd52023-10-19 09:47:40 +02002759 uint16_t next_endpt_index, max_wait;
Michal Vasko9550cf12017-03-21 15:33:58 +01002760 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002761 struct nc_ch_endpt *cur_endpt;
romanf578cd52023-10-19 09:47:40 +02002762 struct nc_session *session = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002763 struct nc_ch_client *client;
romanf578cd52023-10-19 09:47:40 +02002764 uint32_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002765
2766 /* LOCK */
2767 client = nc_server_ch_client_with_endpt_lock(data->client_name);
roman8341e8b2023-11-23 16:12:42 +01002768 if (!client) {
2769 VRB(NULL, "Call Home client \"%s\" removed.", data->client_name);
2770 goto cleanup;
2771 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002772
2773 cur_endpt = &client->ch_endpts[0];
2774 cur_endpt_name = strdup(cur_endpt->name);
2775
Michal Vasko6f865982023-11-21 12:10:42 +01002776 while (nc_server_ch_client_thread_is_running(data)) {
Michal Vasko056f53c2022-10-21 13:38:15 +02002777 if (!cur_attempts) {
2778 VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" connecting...", data->client_name, cur_endpt_name);
2779 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002780
Michal Vasko6f865982023-11-21 12:10:42 +01002781 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 +02002782 if (msgtype == NC_MSG_HELLO) {
2783 /* UNLOCK */
2784 nc_server_ch_client_unlock(client);
2785
romanf578cd52023-10-19 09:47:40 +02002786 if (!nc_server_ch_client_thread_is_running(data)) {
2787 /* thread should stop running */
2788 goto cleanup;
2789 }
2790
2791 /* run while the session is established */
2792 VRB(session, "Call Home client \"%s\" session %u established.", data->client_name, session->id);
roman8341e8b2023-11-23 16:12:42 +01002793 if (nc_server_ch_client_thread_session_cond_wait(data, session)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002794 goto cleanup;
2795 }
roman8341e8b2023-11-23 16:12:42 +01002796 session = NULL;
romanf578cd52023-10-19 09:47:40 +02002797
roman8341e8b2023-11-23 16:12:42 +01002798 VRB(NULL, "Call Home client \"%s\" session terminated.", data->client_name);
romanf578cd52023-10-19 09:47:40 +02002799 if (!nc_server_ch_client_thread_is_running(data)) {
2800 /* thread should stop running */
2801 goto cleanup;
2802 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002803
2804 /* LOCK */
2805 client = nc_server_ch_client_with_endpt_lock(data->client_name);
roman8341e8b2023-11-23 16:12:42 +01002806 if (!client) {
2807 VRB(NULL, "Call Home client \"%s\" removed.", data->client_name);
2808 goto cleanup;
2809 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002810
2811 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002812 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002813 if (client->conn_type == NC_CH_PERIOD) {
romanf578cd52023-10-19 09:47:40 +02002814 if (client->anchor_time) {
Michal Vasko18e1fa02021-11-29 09:02:05 +01002815 /* anchored */
romanf578cd52023-10-19 09:47:40 +02002816 reconnect_in = (time(NULL) - client->anchor_time) % (client->period * 60);
Michal Vasko18e1fa02021-11-29 09:02:05 +01002817 } else {
2818 /* fixed timeout */
romanf578cd52023-10-19 09:47:40 +02002819 reconnect_in = client->period * 60;
Michal Vasko18e1fa02021-11-29 09:02:05 +01002820 }
2821
Michal Vasko2e6defd2016-10-07 15:48:15 +02002822 /* UNLOCK */
2823 nc_server_ch_client_unlock(client);
2824
romanf578cd52023-10-19 09:47:40 +02002825 /* wait for the timeout to elapse, so we can try to reconnect */
2826 VRB(session, "Call Home client \"%s\" reconnecting in %" PRIu32 " seconds.", data->client_name, reconnect_in);
2827 if (!nc_server_ch_client_thread_is_running_wait(session, data, reconnect_in)) {
2828 goto cleanup;
2829 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002830
2831 /* LOCK */
2832 client = nc_server_ch_client_with_endpt_lock(data->client_name);
romanf578cd52023-10-19 09:47:40 +02002833 assert(client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002834 }
2835
2836 /* set next endpoint to try */
2837 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00002838 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002839 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00002840 /* we keep the current one but due to unlock/lock we have to find it again */
2841 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
2842 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
2843 break;
2844 }
2845 }
2846 if (next_endpt_index >= client->ch_endpt_count) {
2847 /* endpoint was removed, start with the first one */
2848 next_endpt_index = 0;
2849 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002850 } else {
2851 /* just get a random index */
2852 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00002853 }
2854
Michal Vasko2e6defd2016-10-07 15:48:15 +02002855 } else {
romanf578cd52023-10-19 09:47:40 +02002856 /* session was not created, wait a little bit and try again */
2857 max_wait = client->max_wait;
2858
Michal Vasko6bb116b2016-10-26 13:53:46 +02002859 /* UNLOCK */
2860 nc_server_ch_client_unlock(client);
2861
romanf578cd52023-10-19 09:47:40 +02002862 /* wait for max_wait seconds */
2863 if (!nc_server_ch_client_thread_is_running_wait(session, data, max_wait)) {
2864 /* thread should stop running */
2865 goto cleanup;
2866 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002867
Michal Vasko6bb116b2016-10-26 13:53:46 +02002868 /* LOCK */
2869 client = nc_server_ch_client_with_endpt_lock(data->client_name);
romanf578cd52023-10-19 09:47:40 +02002870 assert(client);
Michal Vasko6bb116b2016-10-26 13:53:46 +02002871
Michal Vasko2e6defd2016-10-07 15:48:15 +02002872 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02002873
2874 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00002875 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
2876 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02002877 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002878 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02002879 }
2880
Peter Feiged05f2252018-09-03 08:09:47 +00002881 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02002882 /* endpoint was removed, start with the first one */
romanf578cd52023-10-19 09:47:40 +02002883 VRB(session, "Call Home client \"%s\" endpoint \"%s\" removed.", data->client_name, cur_endpt_name);
Peter Feiged05f2252018-09-03 08:09:47 +00002884 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02002885 cur_attempts = 0;
2886 } else if (cur_attempts == client->max_attempts) {
2887 /* we have tried to connect to this endpoint enough times */
romanf578cd52023-10-19 09:47:40 +02002888 VRB(session, "Call Home client \"%s\" endpoint \"%s\" failed connection attempt limit %" PRIu8 " reached.",
Michal Vasko056f53c2022-10-21 13:38:15 +02002889 data->client_name, cur_endpt_name, client->max_attempts);
2890
2891 /* clear a pending socket, if any */
2892 cur_endpt = &client->ch_endpts[next_endpt_index];
2893 if (cur_endpt->sock_pending > -1) {
2894 close(cur_endpt->sock_pending);
2895 cur_endpt->sock_pending = -1;
2896 }
2897
Peter Feiged05f2252018-09-03 08:09:47 +00002898 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002899 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00002900 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002901 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02002902 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02002903 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002904 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002905 cur_attempts = 0;
2906 } /* else we keep the current one */
2907 }
Peter Feiged05f2252018-09-03 08:09:47 +00002908
2909 cur_endpt = &client->ch_endpts[next_endpt_index];
2910 free(cur_endpt_name);
2911 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002912 }
Michal Vasko6f865982023-11-21 12:10:42 +01002913
romanf578cd52023-10-19 09:47:40 +02002914 /* UNLOCK if we break out of the loop */
2915 nc_server_ch_client_unlock(client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002916
2917cleanup:
romanf578cd52023-10-19 09:47:40 +02002918 VRB(session, "Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01002919 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002920 free(data->client_name);
Jan Kundrátfd3a01d2024-04-10 03:03:08 +02002921 pthread_mutex_lock(&data->cond_lock);
roman8341e8b2023-11-23 16:12:42 +01002922 pthread_cond_destroy(&data->cond);
Jan Kundrátfd3a01d2024-04-10 03:03:08 +02002923 pthread_mutex_unlock(&data->cond_lock);
roman8341e8b2023-11-23 16:12:42 +01002924 pthread_mutex_destroy(&data->cond_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002925 free(data);
2926 return NULL;
2927}
2928
2929API int
Michal Vasko93224072021-11-09 12:14:28 +01002930nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
romanf578cd52023-10-19 09:47:40 +02002931 nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb,
2932 void *new_session_cb_data)
Michal Vasko3f05a092018-03-13 10:39:49 +01002933{
Michal Vasko6f865982023-11-21 12:10:42 +01002934 int rc = 0, r;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002935 pthread_t tid;
Michal Vasko6f865982023-11-21 12:10:42 +01002936 struct nc_ch_client_thread_arg *arg = NULL;
romanf578cd52023-10-19 09:47:40 +02002937 struct nc_ch_client *ch_client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002938
romanf578cd52023-10-19 09:47:40 +02002939 NC_CHECK_ARG_RET(NULL, client_name, acquire_ctx_cb, release_ctx_cb, new_session_cb, -1);
2940
romand82caf12024-03-05 14:21:39 +01002941 NC_CHECK_SRV_INIT_RET(-1);
2942
Michal Vasko6f865982023-11-21 12:10:42 +01002943 /* LOCK */
2944 ch_client = nc_server_ch_client_lock(client_name);
2945 if (!ch_client) {
romanf578cd52023-10-19 09:47:40 +02002946 ERR(NULL, "Client \"%s\" not found.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002947 return -1;
2948 }
2949
Michal Vasko6f865982023-11-21 12:10:42 +01002950 /* create the thread argument */
2951 arg = calloc(1, sizeof *arg);
2952 NC_CHECK_ERRMEM_GOTO(!arg, rc = -1, cleanup);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002953 arg->client_name = strdup(client_name);
Michal Vasko6f865982023-11-21 12:10:42 +01002954 NC_CHECK_ERRMEM_GOTO(!arg->client_name, rc = -1, cleanup);
Michal Vasko93224072021-11-09 12:14:28 +01002955 arg->acquire_ctx_cb = acquire_ctx_cb;
2956 arg->release_ctx_cb = release_ctx_cb;
2957 arg->ctx_cb_data = ctx_cb_data;
2958 arg->new_session_cb = new_session_cb;
romanf578cd52023-10-19 09:47:40 +02002959 arg->new_session_cb_data = new_session_cb_data;
romanf578cd52023-10-19 09:47:40 +02002960 pthread_cond_init(&arg->cond, NULL);
romanf578cd52023-10-19 09:47:40 +02002961 pthread_mutex_init(&arg->cond_lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002962
Michal Vasko6f865982023-11-21 12:10:42 +01002963 /* creating the thread */
2964 arg->thread_running = 1;
2965 if ((r = pthread_create(&tid, NULL, nc_ch_client_thread, arg))) {
2966 ERR(NULL, "Creating a new thread failed (%s).", strerror(r));
2967 rc = -1;
2968 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002969 }
Michal Vasko6f865982023-11-21 12:10:42 +01002970
Michal Vasko2e6defd2016-10-07 15:48:15 +02002971 /* the thread now manages arg */
romanf578cd52023-10-19 09:47:40 +02002972 ch_client->tid = tid;
2973 ch_client->thread_data = arg;
Michal Vasko6f865982023-11-21 12:10:42 +01002974 arg = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002975
Michal Vasko6f865982023-11-21 12:10:42 +01002976cleanup:
2977 /* UNLOCK */
2978 nc_server_ch_client_unlock(ch_client);
2979
2980 if (arg) {
2981 free(arg->client_name);
2982 free(arg);
2983 }
2984 return rc;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002985}
2986
romanf578cd52023-10-19 09:47:40 +02002987#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002988
roman44efa322023-11-03 13:57:25 +01002989API struct timespec
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002990nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002991{
roman44efa322023-11-03 13:57:25 +01002992 struct timespec fail = {0};
2993
2994 NC_CHECK_ARG_RET(session, session, fail);
romanf578cd52023-10-19 09:47:40 +02002995
2996 if (session->side != NC_SERVER) {
2997 ERRARG(session, "session");
roman44efa322023-11-03 13:57:25 +01002998 return fail;
Michal Vaskof8352352016-05-24 09:11:36 +02002999 }
3000
Michal Vasko2e6defd2016-10-07 15:48:15 +02003001 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003002}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003003
3004API void
Michal Vasko71dbd772021-03-23 14:08:37 +01003005nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01003006{
3007 if (!session || (session->side != NC_SERVER)) {
romanf578cd52023-10-19 09:47:40 +02003008 ERRARG(session, "session");
Michal Vasko3486a7c2017-03-03 13:28:07 +01003009 return;
3010 }
3011
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003012 /* NTF STATUS LOCK */
3013 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
3014
Michal Vasko71dbd772021-03-23 14:08:37 +01003015 ++session->opts.server.ntf_status;
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003016
3017 /* NTF STATUS UNLOCK */
3018 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko71dbd772021-03-23 14:08:37 +01003019}
3020
3021API void
3022nc_session_dec_notif_status(struct nc_session *session)
3023{
3024 if (!session || (session->side != NC_SERVER)) {
romanf578cd52023-10-19 09:47:40 +02003025 ERRARG(session, "session");
Michal Vasko71dbd772021-03-23 14:08:37 +01003026 return;
3027 }
3028
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003029 /* NTF STATUS LOCK */
3030 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
3031
Michal Vasko71dbd772021-03-23 14:08:37 +01003032 if (session->opts.server.ntf_status) {
3033 --session->opts.server.ntf_status;
3034 }
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003035
3036 /* NTF STATUS UNLOCK */
3037 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003038}
3039
3040API int
3041nc_session_get_notif_status(const struct nc_session *session)
3042{
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003043 uint32_t ntf_status;
3044
Michal Vasko3486a7c2017-03-03 13:28:07 +01003045 if (!session || (session->side != NC_SERVER)) {
romanf578cd52023-10-19 09:47:40 +02003046 ERRARG(session, "session");
Michal Vasko3486a7c2017-03-03 13:28:07 +01003047 return 0;
3048 }
3049
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003050 /* NTF STATUS LOCK */
3051 pthread_mutex_lock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
3052
3053 ntf_status = session->opts.server.ntf_status;
3054
3055 /* NTF STATUS UNLOCK */
3056 pthread_mutex_unlock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
3057
3058 return ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003059}