blob: 564cb63d1733b266aeda11b73d7e03f70fdb6212 [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 Vaskob48aa812016-01-18 14:13:09 +010056};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010057
fanchanghu966f2de2016-07-21 02:28:57 -040058static nc_rpc_clb global_rpc_clb = NULL;
59
Michal Vasko3031aae2016-01-27 16:07:18 +010060struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010061nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010062{
63 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010064 struct nc_endpt *endpt = NULL;
65
romanf578cd52023-10-19 09:47:40 +020066 NC_CHECK_ARG_RET(NULL, name, NULL);
Michal Vaskoddce1212019-05-24 09:58:49 +020067
romanf578cd52023-10-19 09:47:40 +020068 /* READ LOCK */
69 pthread_rwlock_rdlock(&server_opts.config_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010070
71 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020072 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010073 endpt = &server_opts.endpts[i];
74 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010075 }
76 }
77
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010078 if (!endpt) {
Michal Vasko05532772021-06-03 12:12:38 +020079 ERR(NULL, "Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010080 /* UNLOCK */
romanf578cd52023-10-19 09:47:40 +020081 pthread_rwlock_unlock(&server_opts.config_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010082 return NULL;
83 }
84
Michal Vaskoe2713da2016-08-22 16:06:40 +020085 if (idx) {
86 *idx = i;
87 }
88
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010089 return endpt;
90}
91
Michal Vaskoadf30f02019-06-24 09:34:47 +020092struct nc_ch_endpt *
93nc_server_ch_client_lock(const char *name, const char *endpt_name, NC_TRANSPORT_IMPL ti, struct nc_ch_client **client_p)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010094{
Michal Vaskoadf30f02019-06-24 09:34:47 +020095 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020096 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020097 struct nc_ch_endpt *endpt = NULL;
98
99 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200100
romanf578cd52023-10-19 09:47:40 +0200101 NC_CHECK_ARG_RET(NULL, name, NULL);
Michal Vaskoddce1212019-05-24 09:58:49 +0200102
Michal Vasko2e6defd2016-10-07 15:48:15 +0200103 /* READ LOCK */
104 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
105
106 for (i = 0; i < server_opts.ch_client_count; ++i) {
romanf578cd52023-10-19 09:47:40 +0200107 if (server_opts.ch_clients[i].name && !strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200108 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200109 if (!endpt_name && !ti) {
110 /* return only client */
111 break;
112 }
113 for (j = 0; j < client->ch_endpt_count; ++j) {
Michal Vasko530d95c2021-05-28 13:32:02 +0200114 if ((!endpt_name || !strcmp(client->ch_endpts[j].name, endpt_name)) &&
115 (!ti || (ti == client->ch_endpts[j].ti))) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200116 endpt = &client->ch_endpts[j];
117 break;
118 }
119 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200120 break;
121 }
122 }
123
124 if (!client) {
romanf578cd52023-10-19 09:47:40 +0200125 VRB(NULL, "Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200126
Michal Vasko2e6defd2016-10-07 15:48:15 +0200127 /* READ UNLOCK */
128 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200129 } else if (endpt_name && ti && !endpt) {
Michal Vasko05532772021-06-03 12:12:38 +0200130 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200131
132 /* READ UNLOCK */
133 pthread_rwlock_unlock(&server_opts.ch_client_lock);
134 } else {
135 /* CH CLIENT LOCK */
136 pthread_mutex_lock(&client->lock);
137
138 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200139 }
140
Michal Vaskoadf30f02019-06-24 09:34:47 +0200141 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200142}
143
144void
145nc_server_ch_client_unlock(struct nc_ch_client *client)
146{
147 /* CH CLIENT UNLOCK */
148 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100149
150 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200151 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100152}
Michal Vasko086311b2016-01-08 09:53:11 +0100153
roman78df0fa2023-11-02 10:33:57 +0100154int
155nc_server_get_referenced_endpt(const char *name, struct nc_endpt **endpt)
156{
157 uint16_t i;
158
159 for (i = 0; i < server_opts.endpt_count; i++) {
160 if (!strcmp(name, server_opts.endpts[i].name)) {
161 *endpt = &server_opts.endpts[i];
162 return 0;
163 }
164 }
165
166 ERR(NULL, "Referenced endpoint \"%s\" was not found.", name);
167 return 1;
168}
169
Michal Vasko1a38c862016-01-15 15:50:07 +0100170API void
171nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
172{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200173 if (!session) {
romanf578cd52023-10-19 09:47:40 +0200174 ERRARG(session, "session");
Michal Vasko45e53ae2016-04-07 11:46:03 +0200175 return;
176 } else if (!reason) {
romanf578cd52023-10-19 09:47:40 +0200177 ERRARG(session, "reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100178 return;
179 }
180
Michal Vasko142cfea2017-08-07 10:12:11 +0200181 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
182 session->killed_by = 0;
183 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100184 session->term_reason = reason;
185}
186
Michal Vasko142cfea2017-08-07 10:12:11 +0200187API void
188nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
189{
190 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
romanf578cd52023-10-19 09:47:40 +0200191 ERRARG(session, "session");
Michal Vasko142cfea2017-08-07 10:12:11 +0200192 return;
193 } else if (!sid) {
romanf578cd52023-10-19 09:47:40 +0200194 ERRARG(session, "sid");
Michal Vasko142cfea2017-08-07 10:12:11 +0200195 return;
196 }
197
198 session->killed_by = sid;
199}
200
201API void
202nc_session_set_status(struct nc_session *session, NC_STATUS status)
203{
204 if (!session) {
romanf578cd52023-10-19 09:47:40 +0200205 ERRARG(session, "session");
Michal Vasko142cfea2017-08-07 10:12:11 +0200206 return;
207 } else if (!status) {
romanf578cd52023-10-19 09:47:40 +0200208 ERRARG(session, "status");
Michal Vasko142cfea2017-08-07 10:12:11 +0200209 return;
210 }
211
212 session->status = status;
213}
214
romanf578cd52023-10-19 09:47:40 +0200215API int
216nc_server_init_ctx(struct ly_ctx **ctx)
217{
218 int new_ctx = 0, i, ret = 0;
219 struct lys_module *module;
220 /* all features */
221 const char *ietf_netconf_features[] = {"writable-running", "candidate", "rollback-on-error", "validate", "startup", "url", "xpath", "confirmed-commit", NULL};
222 /* all features (module has no features) */
223 const char *ietf_netconf_monitoring_features[] = {NULL};
224
225 NC_CHECK_ARG_RET(NULL, ctx, 1);
226
227 if (!*ctx) {
228 /* context not given, create a new one */
229 if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) {
230 ERR(NULL, "Couldn't create new libyang context.\n");
231 ret = 1;
232 goto cleanup;
233 }
234 new_ctx = 1;
235 }
236
237 if (new_ctx) {
238 /* new context created, implement both modules */
239 if (!ly_ctx_load_module(*ctx, "ietf-netconf", NULL, ietf_netconf_features)) {
240 ERR(NULL, "Loading module \"ietf-netconf\" failed.\n");
241 ret = 1;
242 goto cleanup;
243 }
244
245 if (!ly_ctx_load_module(*ctx, "ietf-netconf-monitoring", NULL, ietf_netconf_monitoring_features)) {
246 ERR(NULL, "Loading module \"ietf-netconf-monitoring\" failed.\n");
247 ret = 1;
248 goto cleanup;
249 }
250
251 goto cleanup;
252 }
253
254 module = ly_ctx_get_module_implemented(*ctx, "ietf-netconf");
255 if (module) {
256 /* ietf-netconf module is present, check features */
257 for (i = 0; ietf_netconf_features[i]; i++) {
258 if (lys_feature_value(module, ietf_netconf_features[i])) {
259 /* feature not found, enable all of them */
260 if (!ly_ctx_load_module(*ctx, "ietf-netconf", NULL, ietf_netconf_features)) {
261 ERR(NULL, "Loading module \"ietf-netconf\" failed.\n");
262 ret = 1;
263 goto cleanup;
264 }
265
266 break;
267 }
268 }
269 } else {
270 /* ietf-netconf module not found, add it */
271 if (!ly_ctx_load_module(*ctx, "ietf-netconf", NULL, ietf_netconf_features)) {
272 ERR(NULL, "Loading module \"ietf-netconf\" failed.\n");
273 ret = 1;
274 goto cleanup;
275 }
276 }
277
278 module = ly_ctx_get_module_implemented(*ctx, "ietf-netconf-monitoring");
279 if (!module) {
280 /* ietf-netconf-monitoring module not found, add it */
281 if (!ly_ctx_load_module(*ctx, "ietf-netconf-monitoring", NULL, ietf_netconf_monitoring_features)) {
282 ERR(NULL, "Loading module \"ietf-netconf-monitoring\" failed.\n");
283 ret = 1;
284 goto cleanup;
285 }
286 }
287
288cleanup:
289 if (new_ctx && ret) {
290 ly_ctx_destroy(*ctx);
291 *ctx = NULL;
292 }
293 return ret;
294}
295
roman96c27f92023-11-02 11:09:46 +0100296#ifdef NC_ENABLED_SSH_TLS
297
roman450c00b2023-11-02 10:31:45 +0100298API void
299nc_server_ch_set_dispatch_data(nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
300 nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb,
301 void *new_session_cb_data)
302{
303 NC_CHECK_ARG_RET(NULL, acquire_ctx_cb, release_ctx_cb, new_session_cb, );
304
305 server_opts.ch_dispatch_data.acquire_ctx_cb = acquire_ctx_cb;
306 server_opts.ch_dispatch_data.release_ctx_cb = release_ctx_cb;
307 server_opts.ch_dispatch_data.ctx_cb_data = ctx_cb_data;
308 server_opts.ch_dispatch_data.new_session_cb = new_session_cb;
309 server_opts.ch_dispatch_data.new_session_cb_data = new_session_cb_data;
310}
311
roman96c27f92023-11-02 11:09:46 +0100312#endif
313
Michal Vasko086311b2016-01-08 09:53:11 +0100314int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200315nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100316{
Michal Vasko06c860d2018-07-09 16:08:52 +0200317 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100318 int is_ipv4, sock;
319 struct sockaddr_storage saddr;
320
321 struct sockaddr_in *saddr4;
322 struct sockaddr_in6 *saddr6;
323
Michal Vasko086311b2016-01-08 09:53:11 +0100324 if (!strchr(address, ':')) {
325 is_ipv4 = 1;
326 } else {
327 is_ipv4 = 0;
328 }
329
330 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
331 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200332 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100333 goto fail;
334 }
335
Michal Vaskobe52dc22018-10-17 09:28:17 +0200336 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200337 opt = 1;
338 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200339 ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko06c860d2018-07-09 16:08:52 +0200340 goto fail;
341 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100342 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200343 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vasko83ad17e2019-01-30 10:11:37 +0100344 goto fail;
345 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200346
romanf578cd52023-10-19 09:47:40 +0200347 if (nc_sock_configure_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100348 goto fail;
349 }
350
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200351 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100352 if (is_ipv4) {
353 saddr4 = (struct sockaddr_in *)&saddr;
354
355 saddr4->sin_family = AF_INET;
356 saddr4->sin_port = htons(port);
357
358 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200359 ERR(NULL, "Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100360 goto fail;
361 }
362
363 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200364 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100365 goto fail;
366 }
367
368 } else {
369 saddr6 = (struct sockaddr_in6 *)&saddr;
370
371 saddr6->sin6_family = AF_INET6;
372 saddr6->sin6_port = htons(port);
373
374 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200375 ERR(NULL, "Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100376 goto fail;
377 }
378
379 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200380 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100381 goto fail;
382 }
383 }
384
Michal Vaskofb89d772016-01-08 12:25:35 +0100385 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200386 ERR(NULL, "Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100387 goto fail;
388 }
Michal Vasko086311b2016-01-08 09:53:11 +0100389 return sock;
390
391fail:
392 if (sock > -1) {
393 close(sock);
394 }
395
396 return -1;
397}
398
399int
roman83683fb2023-02-24 09:15:23 +0100400nc_sock_listen_unix(const struct nc_server_unix_opts *opts)
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200401{
402 struct sockaddr_un sun;
403 int sock = -1;
404
roman83683fb2023-02-24 09:15:23 +0100405 if (strlen(opts->address) > sizeof(sun.sun_path) - 1) {
406 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 +0200407 goto fail;
408 }
409
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200410 sock = socket(AF_UNIX, SOCK_STREAM, 0);
411 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200412 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200413 goto fail;
414 }
415
416 memset(&sun, 0, sizeof(sun));
417 sun.sun_family = AF_UNIX;
roman83683fb2023-02-24 09:15:23 +0100418 snprintf(sun.sun_path, sizeof(sun.sun_path) - 1, "%s", opts->address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200419
420 unlink(sun.sun_path);
421 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
roman83683fb2023-02-24 09:15:23 +0100422 ERR(NULL, "Could not bind \"%s\" (%s).", opts->address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200423 goto fail;
424 }
425
426 if (opts->mode != (mode_t)-1) {
427 if (chmod(sun.sun_path, opts->mode) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200428 ERR(NULL, "Failed to set unix socket permissions (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200429 goto fail;
430 }
431 }
432
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200433 if ((opts->uid != (uid_t)-1) || (opts->gid != (gid_t)-1)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200434 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200435 ERR(NULL, "Failed to set unix socket uid/gid (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200436 goto fail;
437 }
438 }
439
440 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
roman83683fb2023-02-24 09:15:23 +0100441 ERR(NULL, "Unable to start listening on \"%s\" (%s).", opts->address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200442 goto fail;
443 }
444
445 return sock;
446
447fail:
448 if (sock > -1) {
449 close(sock);
450 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200451 return -1;
452}
453
aPiecek90ff0242021-02-14 14:58:01 +0100454/**
455 * @brief Evaluate socket name for AF_UNIX socket.
456 * @param[in] acc_sock_fd is file descriptor for the accepted socket (a nonnegative).
457 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
458 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
459 * @return 0 if the stream socket is unnamed. Parameter host is set to NULL.
460 * @return -1 in case of error. Parameter host is set to NULL.
461 */
462static int
463sock_host_unix(int acc_sock_fd, char **host)
464{
465 char *sun_path;
466 struct sockaddr_storage saddr;
467 socklen_t addr_len;
468
469 *host = NULL;
470 saddr.ss_family = AF_UNIX;
471 addr_len = sizeof(saddr);
472
473 if (getsockname(acc_sock_fd, (struct sockaddr *)&saddr, &addr_len)) {
Michal Vasko05532772021-06-03 12:12:38 +0200474 ERR(NULL, "getsockname failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100475 return -1;
476 }
477
478 sun_path = ((struct sockaddr_un *)&saddr)->sun_path;
479 if (!sun_path) {
480 /* stream socket is unnamed */
481 return 0;
482 }
483
roman3a95bb22023-10-26 11:07:17 +0200484 NC_CHECK_ERRMEM_RET(!(*host = strdup(sun_path)), -1);
aPiecek90ff0242021-02-14 14:58:01 +0100485
486 return 0;
487}
488
489/**
490 * @brief Evaluate socket name and port number for AF_INET socket.
491 * @param[in] addr is pointing to structure filled by accept function which was successful.
492 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
493 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
494 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
495 * @return -1 in case of error. Parameter host is set to NULL and port is unchanged.
496 */
497static int
498sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port)
499{
500 *host = malloc(INET_ADDRSTRLEN);
roman3a95bb22023-10-26 11:07:17 +0200501 NC_CHECK_ERRMEM_RET(!(*host), -1);
aPiecek90ff0242021-02-14 14:58:01 +0100502
aPiecek3da9b342021-02-18 15:00:03 +0100503 if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko69e98752022-12-14 14:20:17 +0100504 ERR(NULL, "inet_ntop failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100505 free(*host);
506 *host = NULL;
507 return -1;
508 }
509
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200510 *port = ntohs(addr->sin_port);
aPiecek90ff0242021-02-14 14:58:01 +0100511
512 return 0;
513}
514
515/**
516 * @brief Evaluate socket name and port number for AF_INET6 socket.
517 * @param[in] addr is pointing to structure filled by accept function which was successful.
518 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
519 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
520 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
521 * @return -1 in case of error. Parameter host is set to the NULL and port is unchanged.
522 */
523static int
524sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port)
525{
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200526 *host = malloc(INET6_ADDRSTRLEN);
roman3a95bb22023-10-26 11:07:17 +0200527 NC_CHECK_ERRMEM_RET(!(*host), -1);
aPiecek90ff0242021-02-14 14:58:01 +0100528
aPiecek3da9b342021-02-18 15:00:03 +0100529 if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko69e98752022-12-14 14:20:17 +0100530 ERR(NULL, "inet_ntop failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100531 free(*host);
532 *host = NULL;
533 return -1;
534 }
535
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200536 *port = ntohs(addr->sin6_port);
aPiecek90ff0242021-02-14 14:58:01 +0100537
538 return 0;
539}
540
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200541int
romanf578cd52023-10-19 09:47:40 +0200542nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, pthread_mutex_t *bind_lock, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100543{
Michal Vaskof54cd352017-02-22 13:42:02 +0100544 sigset_t sigmask, origmask;
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200545 uint16_t i, j, pfd_count, client_port;
546 char *client_address;
Michal Vasko086311b2016-01-08 09:53:11 +0100547 struct pollfd *pfd;
548 struct sockaddr_storage saddr;
549 socklen_t saddr_len = sizeof(saddr);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200550 int ret, client_sock, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100551
552 pfd = malloc(bind_count * sizeof *pfd);
roman3a95bb22023-10-26 11:07:17 +0200553 NC_CHECK_ERRMEM_RET(!pfd, -1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100554
romanf578cd52023-10-19 09:47:40 +0200555 /* LOCK */
556 pthread_mutex_lock(bind_lock);
557
Michal Vaskoac2f6182017-01-30 14:32:03 +0100558 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200559 if (binds[i].sock < 0) {
560 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200561 continue;
562 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200563 if (binds[i].pollin) {
564 binds[i].pollin = 0;
565 /* leftover pollin */
566 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100567 break;
568 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100569 pfd[pfd_count].fd = binds[i].sock;
570 pfd[pfd_count].events = POLLIN;
571 pfd[pfd_count].revents = 0;
572
573 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100574 }
575
Michal Vasko0a3f3752016-10-13 14:58:38 +0200576 if (sock == -1) {
577 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100578 sigfillset(&sigmask);
579 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100580 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100581 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
582
Michal Vasko0a3f3752016-10-13 14:58:38 +0200583 if (!ret) {
584 /* we timeouted */
585 free(pfd);
romanf578cd52023-10-19 09:47:40 +0200586 /* UNLOCK */
587 pthread_mutex_unlock(bind_lock);
Michal Vasko0a3f3752016-10-13 14:58:38 +0200588 return 0;
589 } else if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200590 ERR(NULL, "Poll failed (%s).", strerror(errno));
Michal Vasko0a3f3752016-10-13 14:58:38 +0200591 free(pfd);
romanf578cd52023-10-19 09:47:40 +0200592 /* UNLOCK */
593 pthread_mutex_unlock(bind_lock);
Michal Vasko0a3f3752016-10-13 14:58:38 +0200594 return -1;
595 }
Michal Vasko086311b2016-01-08 09:53:11 +0100596
Michal Vaskoac2f6182017-01-30 14:32:03 +0100597 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
598 /* adjust i so that indices in binds and pfd always match */
599 while (binds[i].sock != pfd[j].fd) {
600 ++i;
601 }
602
603 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200604 --ret;
605
606 if (!ret) {
607 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100608 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200609 break;
610 } else {
611 /* just remember the event for next time */
612 binds[i].pollin = 1;
613 }
614 }
Michal Vasko086311b2016-01-08 09:53:11 +0100615 }
616 }
617 free(pfd);
Michal Vasko086311b2016-01-08 09:53:11 +0100618 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100619 ERRINT;
romanf578cd52023-10-19 09:47:40 +0200620 /* UNLOCK */
621 pthread_mutex_unlock(bind_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100622 return -1;
623 }
624
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200625 /* accept connection */
626 client_sock = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
627 if (client_sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200628 ERR(NULL, "Accept failed (%s).", strerror(errno));
romanf578cd52023-10-19 09:47:40 +0200629 /* UNLOCK */
630 pthread_mutex_unlock(bind_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100631 return -1;
632 }
633
Michal Vasko0190bc32016-03-02 15:47:49 +0100634 /* make the socket non-blocking */
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200635 if (((flags = fcntl(client_sock, F_GETFL)) == -1) || (fcntl(client_sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200636 ERR(NULL, "Fcntl failed (%s).", strerror(errno));
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200637 goto fail;
Michal Vasko0190bc32016-03-02 15:47:49 +0100638 }
639
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200640 /* learn information about the client end */
641 if (saddr.ss_family == AF_UNIX) {
642 if (sock_host_unix(client_sock, &client_address)) {
643 goto fail;
644 }
645 client_port = 0;
646 } else if (saddr.ss_family == AF_INET) {
647 if (sock_host_inet((struct sockaddr_in *)&saddr, &client_address, &client_port)) {
648 goto fail;
649 }
650 } else if (saddr.ss_family == AF_INET6) {
651 if (sock_host_inet6((struct sockaddr_in6 *)&saddr, &client_address, &client_port)) {
652 goto fail;
653 }
654 } else {
655 ERR(NULL, "Source host of an unknown protocol family.");
656 goto fail;
aPiecek90ff0242021-02-14 14:58:01 +0100657 }
Michal Vasko086311b2016-01-08 09:53:11 +0100658
aPiecek90ff0242021-02-14 14:58:01 +0100659 if (saddr.ss_family == AF_UNIX) {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200660 VRB(NULL, "Accepted a connection on %s.", binds[i].address);
aPiecek90ff0242021-02-14 14:58:01 +0100661 } else {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200662 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 +0100663 }
664
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200665 if (host) {
666 *host = client_address;
667 } else {
668 free(client_address);
669 }
670 if (port) {
671 *port = client_port;
672 }
673 if (idx) {
674 *idx = i;
675 }
romanf578cd52023-10-19 09:47:40 +0200676 /* UNLOCK */
677 pthread_mutex_unlock(bind_lock);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200678 return client_sock;
679
680fail:
681 close(client_sock);
romanf578cd52023-10-19 09:47:40 +0200682 /* UNLOCK */
683 pthread_mutex_unlock(bind_lock);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200684 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100685}
686
Michal Vasko238b6c12021-12-14 15:14:09 +0100687API struct nc_server_reply *
Michal Vasko05532772021-06-03 12:12:38 +0200688nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100689{
Michal Vasko77367452021-02-16 16:32:18 +0100690 const char *identifier = NULL, *revision = NULL, *format = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100691 char *model_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100692 struct ly_out *out;
Michal Vasko9b1a9522021-03-15 16:24:26 +0100693 const struct lys_module *module = NULL, *mod;
Michal Vasko77367452021-02-16 16:32:18 +0100694 const struct lysp_submodule *submodule = NULL;
695 struct lyd_node *child, *err, *data = NULL;
696 LYS_OUTFORMAT outformat = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100697
Michal Vasko77367452021-02-16 16:32:18 +0100698 LY_LIST_FOR(lyd_child(rpc), child) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100699 if (!strcmp(child->schema->name, "identifier")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200700 identifier = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100701 } else if (!strcmp(child->schema->name, "version")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200702 revision = lyd_get_value(child);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200703 if (revision && (revision[0] == '\0')) {
Michal Vasko77367452021-02-16 16:32:18 +0100704 revision = NULL;
Radek Krejci1afa7792017-03-26 11:24:16 -0500705 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100706 } else if (!strcmp(child->schema->name, "format")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200707 format = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100708 }
709 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200710 VRB(session, "Module \"%s@%s\" was requested.", identifier, revision ? revision : "<any>");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100711
Michal Vasko77367452021-02-16 16:32:18 +0100712 /* check revision */
713 if (revision && (strlen(revision) != 10) && strcmp(revision, "1.0")) {
Michal Vasko93224072021-11-09 12:14:28 +0100714 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100715 nc_err_set_msg(err, "The requested version is not supported.", "en");
716 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100717 }
718
Michal Vasko77367452021-02-16 16:32:18 +0100719 if (revision) {
720 /* get specific module */
Michal Vasko93224072021-11-09 12:14:28 +0100721 module = ly_ctx_get_module(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100722 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100723 submodule = ly_ctx_get_submodule(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100724 }
725 } else {
726 /* try to get implemented, then latest module */
Michal Vasko93224072021-11-09 12:14:28 +0100727 module = ly_ctx_get_module_implemented(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100728 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100729 module = ly_ctx_get_module_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100730 }
731 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100732 submodule = ly_ctx_get_submodule_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100733 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200734 }
Michal Vasko77367452021-02-16 16:32:18 +0100735 if (!module && !submodule) {
Michal Vasko93224072021-11-09 12:14:28 +0100736 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko5ca5d972022-09-14 13:51:31 +0200737 nc_err_set_msg(err, "The requested module was not found.", "en");
Michal Vasko1a38c862016-01-15 15:50:07 +0100738 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100739 }
740
741 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100742 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko77367452021-02-16 16:32:18 +0100743 outformat = LYS_OUT_YANG;
Radek Krejci90fba642016-12-07 15:59:45 +0100744 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko77367452021-02-16 16:32:18 +0100745 outformat = LYS_OUT_YIN;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100746 } else {
Michal Vasko93224072021-11-09 12:14:28 +0100747 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100748 nc_err_set_msg(err, "The requested format is not supported.", "en");
749 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100750 }
Michal Vasko77367452021-02-16 16:32:18 +0100751
752 /* print */
753 ly_out_new_memory(&model_data, 0, &out);
754 if (module) {
755 lys_print_module(out, module, outformat, 0, 0);
756 } else {
757 lys_print_submodule(out, submodule, outformat, 0, 0);
758 }
759 ly_out_free(out, NULL, 0);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200760 if (!model_data) {
761 ERRINT;
762 return NULL;
763 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100764
Michal Vasko9b1a9522021-03-15 16:24:26 +0100765 /* create reply */
Michal Vasko93224072021-11-09 12:14:28 +0100766 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vasko9b1a9522021-03-15 16:24:26 +0100767 if (!mod || lyd_new_inner(NULL, mod, "get-schema", 0, &data)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100768 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200769 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100770 return NULL;
771 }
Michal Vasko9b1a9522021-03-15 16:24:26 +0100772 if (lyd_new_any(data, NULL, "data", model_data, 1, LYD_ANYDATA_STRING, 1, NULL)) {
773 ERRINT;
Michal Vaskoa50f68e2022-02-24 16:10:54 +0100774 free(model_data);
Michal Vasko9b1a9522021-03-15 16:24:26 +0100775 lyd_free_tree(data);
776 return NULL;
777 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100778
Radek Krejci36dfdb32016-09-01 16:56:35 +0200779 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100780}
781
Michal Vasko238b6c12021-12-14 15:14:09 +0100782API struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100783nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100784{
Michal Vasko428087d2016-01-14 16:04:28 +0100785 session->term_reason = NC_SESSION_TERM_CLOSED;
786 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100787}
788
Michal Vasko93224072021-11-09 12:14:28 +0100789/**
790 * @brief Initialize a context with default RPC callbacks if none are set.
791 *
792 * @param[in] ctx Context to initialize.
793 */
794static void
romanf578cd52023-10-19 09:47:40 +0200795nc_server_init_cb_ctx(const struct ly_ctx *ctx)
Michal Vasko086311b2016-01-08 09:53:11 +0100796{
Michal Vasko77367452021-02-16 16:32:18 +0100797 struct lysc_node *rpc;
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100798
Michal Vasko238b6c12021-12-14 15:14:09 +0100799 if (global_rpc_clb) {
800 /* expect it to handle these RPCs as well */
801 return;
802 }
803
Michal Vasko05ba9df2016-01-13 14:40:27 +0100804 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100805 rpc = NULL;
806 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
807 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
808 }
Michal Vasko88639e92017-08-03 14:38:10 +0200809 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100810 rpc->priv = nc_clb_default_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100811 }
812
Michal Vasko93224072021-11-09 12:14:28 +0100813 /* set default <close-session> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100814 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200815 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100816 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100817 }
Michal Vasko93224072021-11-09 12:14:28 +0100818}
Michal Vasko05ba9df2016-01-13 14:40:27 +0100819
Michal Vasko93224072021-11-09 12:14:28 +0100820API int
821nc_server_init(void)
822{
823 pthread_rwlockattr_t attr, *attr_p = NULL;
824 int r;
825
Michal Vaskob48aa812016-01-18 14:13:09 +0100826 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500827 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100828
Michal Vasko93224072021-11-09 12:14:28 +0100829#ifdef HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
830 if ((r = pthread_rwlockattr_init(&attr))) {
831 ERR(NULL, "%s: failed init attribute (%s).", __func__, strerror(r));
832 goto error;
833 }
834 attr_p = &attr;
835 if ((r = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))) {
836 ERR(NULL, "%s: failed set attribute (%s).", __func__, strerror(r));
837 goto error;
838 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700839#endif
Michal Vasko93224072021-11-09 12:14:28 +0100840
romanf578cd52023-10-19 09:47:40 +0200841 if ((r = pthread_rwlock_init(&server_opts.config_lock, attr_p))) {
Michal Vasko93224072021-11-09 12:14:28 +0100842 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
843 goto error;
844 }
845 if ((r = pthread_rwlock_init(&server_opts.ch_client_lock, attr_p))) {
846 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
847 goto error;
848 }
849
850 if (attr_p) {
851 pthread_rwlockattr_destroy(attr_p);
Frank Rimpler9f838b02018-07-25 06:44:03 +0000852 }
romanf578cd52023-10-19 09:47:40 +0200853
854#ifdef NC_ENABLED_SSH_TLS
855 if (curl_global_init(CURL_GLOBAL_SSL | CURL_GLOBAL_ACK_EINTR)) {
856 ERR(NULL, "%s: failed to init CURL.", __func__);
857 goto error;
858 }
859#endif
860
861 if ((r = pthread_mutex_init(&server_opts.bind_lock, NULL))) {
862 ERR(NULL, "%s: failed to init bind lock(%s).", __func__, strerror(r));
863 goto error;
864 }
865
Michal Vasko086311b2016-01-08 09:53:11 +0100866 return 0;
Michal Vasko93224072021-11-09 12:14:28 +0100867
868error:
869 if (attr_p) {
870 pthread_rwlockattr_destroy(attr_p);
871 }
872 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100873}
874
Michal Vaskob48aa812016-01-18 14:13:09 +0100875API void
876nc_server_destroy(void)
877{
Michal Vasko1440a742021-03-31 11:11:03 +0200878 uint32_t i;
Radek Krejci658782b2016-12-04 22:04:55 +0100879
880 for (i = 0; i < server_opts.capabilities_count; i++) {
Michal Vasko93224072021-11-09 12:14:28 +0100881 free(server_opts.capabilities[i]);
Radek Krejci658782b2016-12-04 22:04:55 +0100882 }
883 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200884 server_opts.capabilities = NULL;
885 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200886 if (server_opts.content_id_data && server_opts.content_id_data_free) {
887 server_opts.content_id_data_free(server_opts.content_id_data);
888 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200889
romanf578cd52023-10-19 09:47:40 +0200890 nc_server_config_listen(NULL, NC_OP_DELETE);
891 nc_server_config_ch(NULL, NC_OP_DELETE);
892
893 pthread_mutex_destroy(&server_opts.bind_lock);
894
895#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoebba7602018-03-23 13:14:08 +0100896 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
897 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
898 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200899 server_opts.passwd_auth_data = NULL;
900 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100901
Michal Vasko1c2d2652023-10-17 08:53:36 +0200902 if (server_opts.pubkey_auth_data && server_opts.pubkey_auth_data_free) {
903 server_opts.pubkey_auth_data_free(server_opts.pubkey_auth_data);
904 }
905 server_opts.pubkey_auth_data = NULL;
906 server_opts.pubkey_auth_data_free = NULL;
907
Michal Vasko1c2d2652023-10-17 08:53:36 +0200908 if (server_opts.interactive_auth_data && server_opts.interactive_auth_data_free) {
909 server_opts.interactive_auth_data_free(server_opts.interactive_auth_data);
910 }
911 server_opts.interactive_auth_data = NULL;
912 server_opts.interactive_auth_data_free = NULL;
913
romanf578cd52023-10-19 09:47:40 +0200914 nc_server_config_ks_keystore(NULL, NC_OP_DELETE);
915 nc_server_config_ts_truststore(NULL, NC_OP_DELETE);
916 curl_global_cleanup();
917#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskob48aa812016-01-18 14:13:09 +0100918}
919
Michal Vasko086311b2016-01-08 09:53:11 +0100920API int
921nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
922{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200923 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
romanf578cd52023-10-19 09:47:40 +0200924 ERRARG(NULL, "basic_mode");
Michal Vasko45e53ae2016-04-07 11:46:03 +0200925 return -1;
926 } 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 +0200927 ERRARG(NULL, "also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100928 return -1;
929 }
930
romanf578cd52023-10-19 09:47:40 +0200931 ATOMIC_STORE_RELAXED(server_opts.wd_basic_mode, basic_mode);
932 ATOMIC_STORE_RELAXED(server_opts.wd_also_supported, also_supported);
Michal Vasko086311b2016-01-08 09:53:11 +0100933 return 0;
934}
935
Michal Vasko1a38c862016-01-15 15:50:07 +0100936API void
Michal Vasko55f03972016-04-13 08:56:01 +0200937nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
938{
939 if (!basic_mode && !also_supported) {
romanf578cd52023-10-19 09:47:40 +0200940 ERRARG(NULL, "basic_mode and also_supported");
Michal Vasko55f03972016-04-13 08:56:01 +0200941 return;
942 }
943
944 if (basic_mode) {
romanf578cd52023-10-19 09:47:40 +0200945 *basic_mode = ATOMIC_LOAD_RELAXED(server_opts.wd_basic_mode);
Michal Vasko55f03972016-04-13 08:56:01 +0200946 }
947 if (also_supported) {
romanf578cd52023-10-19 09:47:40 +0200948 *also_supported = ATOMIC_LOAD_RELAXED(server_opts.wd_also_supported);
Michal Vasko55f03972016-04-13 08:56:01 +0200949 }
950}
951
Michal Vasko55f03972016-04-13 08:56:01 +0200952API int
Radek Krejci658782b2016-12-04 22:04:55 +0100953nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200954{
Michal Vasko93224072021-11-09 12:14:28 +0100955 void *mem;
Radek Krejci658782b2016-12-04 22:04:55 +0100956
957 if (!value || !value[0]) {
romanf578cd52023-10-19 09:47:40 +0200958 ERRARG(NULL, "value must not be empty");
Radek Krejci658782b2016-12-04 22:04:55 +0100959 return EXIT_FAILURE;
960 }
961
Michal Vasko93224072021-11-09 12:14:28 +0100962 mem = realloc(server_opts.capabilities, (server_opts.capabilities_count + 1) * sizeof *server_opts.capabilities);
roman3a95bb22023-10-26 11:07:17 +0200963 NC_CHECK_ERRMEM_RET(!mem, EXIT_FAILURE);
Michal Vasko93224072021-11-09 12:14:28 +0100964 server_opts.capabilities = mem;
965
966 server_opts.capabilities[server_opts.capabilities_count] = strdup(value);
967 server_opts.capabilities_count++;
Radek Krejci658782b2016-12-04 22:04:55 +0100968
969 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200970}
971
Michal Vasko1a38c862016-01-15 15:50:07 +0100972API void
Michal Vasko1440a742021-03-31 11:11:03 +0200973nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
974 void (*free_user_data)(void *user_data))
975{
976 server_opts.content_id_clb = content_id_clb;
977 server_opts.content_id_data = user_data;
978 server_opts.content_id_data_free = free_user_data;
979}
980
Michal Vasko71090fc2016-05-24 16:37:28 +0200981API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +0100982nc_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 +0100983{
Michal Vasko71090fc2016-05-24 16:37:28 +0200984 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200985 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200986
romanf578cd52023-10-19 09:47:40 +0200987 NC_CHECK_ARG_RET(NULL, ctx, username, session, NC_MSG_ERROR);
988
989 if (fdin < 0) {
990 ERRARG(NULL, "fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200991 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200992 } else if (fdout < 0) {
romanf578cd52023-10-19 09:47:40 +0200993 ERRARG(NULL, "fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200994 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100995 }
996
Michal Vasko93224072021-11-09 12:14:28 +0100997 /* init ctx as needed */
romanf578cd52023-10-19 09:47:40 +0200998 nc_server_init_cb_ctx(ctx);
Michal Vasko93224072021-11-09 12:14:28 +0100999
Michal Vasko086311b2016-01-08 09:53:11 +01001000 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001001 *session = nc_new_session(NC_SERVER, 0);
roman3a95bb22023-10-26 11:07:17 +02001002 NC_CHECK_ERRMEM_RET(!(*session), NC_MSG_ERROR);
Michal Vasko1a38c862016-01-15 15:50:07 +01001003 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +01001004
Michal Vasko086311b2016-01-08 09:53:11 +01001005 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +01001006 (*session)->ti_type = NC_TI_FD;
1007 (*session)->ti.fd.in = fdin;
1008 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +01001009
Michal Vasko93224072021-11-09 12:14:28 +01001010 /* assign context */
Michal Vasko1a38c862016-01-15 15:50:07 +01001011 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01001012 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001013
Michal Vaskob48aa812016-01-18 14:13:09 +01001014 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001015 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01001016
Michal Vasko086311b2016-01-08 09:53:11 +01001017 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001018 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001019 if (msgtype != NC_MSG_HELLO) {
1020 nc_session_free(*session, NULL);
1021 *session = NULL;
1022 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001023 }
Michal Vasko9fb42272017-10-05 13:50:05 +02001024
Michal Vaskod8a74192023-02-06 15:51:50 +01001025 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02001026 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +01001027 nc_realtime_get(&ts_cur);
Michal Vasko9fb42272017-10-05 13:50:05 +02001028 (*session)->opts.server.session_start = ts_cur.tv_sec;
1029
Michal Vasko1a38c862016-01-15 15:50:07 +01001030 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +01001031
Michal Vasko71090fc2016-05-24 16:37:28 +02001032 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001033}
Michal Vasko9e036d52016-01-08 10:49:26 +01001034
Michal Vaskob30b99c2016-07-26 11:35:43 +02001035static void
Michal Vasko74c345f2018-02-07 10:37:11 +01001036nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
1037{
1038 uint8_t q_last;
1039
1040 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
1041 ERRINT;
1042 return;
1043 }
1044
1045 /* get a unique queue value (by adding 1 to the last added value, if any) */
1046 if (ps->queue_len) {
1047 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
1048 *id = ps->queue[q_last] + 1;
1049 } else {
1050 *id = 0;
1051 }
1052
1053 /* add the id into the queue */
1054 ++ps->queue_len;
1055 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
1056 ps->queue[q_last] = *id;
1057}
1058
1059static void
Michal Vaskob30b99c2016-07-26 11:35:43 +02001060nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
1061{
Michal Vasko74c345f2018-02-07 10:37:11 +01001062 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001063
1064 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +01001065 /* get the actual queue idx */
1066 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001067
1068 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +01001069 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +02001070 /* another equal value, simply cannot be */
1071 ERRINT;
1072 }
Michal Vaskod8340032018-02-12 14:41:00 +01001073 if (found == 2) {
1074 /* move the following values */
1075 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
1076 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001077 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +02001078 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +01001079 if (i == 0) {
1080 found = 1;
1081 } else {
1082 /* this is not okay, our id is in the middle of the queue */
1083 found = 2;
1084 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001085 }
1086 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001087 if (!found) {
1088 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +01001089 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001090 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001091
Michal Vasko103fe632018-02-12 16:37:45 +01001092 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +01001093 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +01001094 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +01001095 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
1096 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001097}
1098
Michal Vaskof04a52a2016-04-07 10:52:10 +02001099int
Michal Vasko26043172016-07-26 14:08:59 +02001100nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001101{
1102 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001103 struct timespec ts;
1104
Michal Vaskobe86fe32016-04-07 10:43:03 +02001105 /* LOCK */
Michal Vasko8c7def52023-06-06 14:48:56 +02001106 ret = pthread_mutex_lock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001107 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001108 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001109 return -1;
1110 }
1111
Michal Vasko74c345f2018-02-07 10:37:11 +01001112 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +01001113 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko05532772021-06-03 12:12:38 +02001114 ERR(NULL, "%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +01001115 pthread_mutex_unlock(&ps->lock);
1116 return -1;
1117 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001118
1119 /* add ourselves into the queue */
1120 nc_ps_queue_add_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001121 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 +02001122 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001123
1124 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001125 while (ps->queue[ps->queue_begin] != *id) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001126 nc_timeouttime_get(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001127
Michal Vaskod8a74192023-02-06 15:51:50 +01001128 ret = pthread_cond_clockwait(&ps->cond, &ps->lock, COMPAT_CLOCK_ID, &ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001129 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301130 /**
1131 * This may happen when another thread releases the lock and broadcasts the condition
1132 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1133 * but when actually this thread was ready for condition.
1134 */
preetbhansali629dfc42018-12-17 16:04:40 +05301135 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301136 break;
1137 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001138
Michal Vasko05532772021-06-03 12:12:38 +02001139 ERR(NULL, "%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001140 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001141 nc_ps_queue_remove_id(ps, *id);
1142 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001143 return -1;
1144 }
1145 }
1146
Michal Vaskobe86fe32016-04-07 10:43:03 +02001147 /* UNLOCK */
1148 pthread_mutex_unlock(&ps->lock);
1149
1150 return 0;
1151}
1152
Michal Vaskof04a52a2016-04-07 10:52:10 +02001153int
Michal Vasko26043172016-07-26 14:08:59 +02001154nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001155{
1156 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001157
1158 /* LOCK */
Michal Vasko8c7def52023-06-06 14:48:56 +02001159 ret = pthread_mutex_lock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001160 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001161 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001162 ret = -1;
1163 }
1164
Michal Vaskob30b99c2016-07-26 11:35:43 +02001165 /* we must be the first, it was our turn after all, right? */
1166 if (ps->queue[ps->queue_begin] != id) {
1167 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001168 /* UNLOCK */
1169 if (!ret) {
1170 pthread_mutex_unlock(&ps->lock);
1171 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001172 return -1;
1173 }
1174
Michal Vaskobe86fe32016-04-07 10:43:03 +02001175 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001176 nc_ps_queue_remove_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001177 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 +02001178 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001179
1180 /* broadcast to all other threads that the queue moved */
1181 pthread_cond_broadcast(&ps->cond);
1182
Michal Vaskobe86fe32016-04-07 10:43:03 +02001183 /* UNLOCK */
1184 if (!ret) {
1185 pthread_mutex_unlock(&ps->lock);
1186 }
1187
1188 return ret;
1189}
1190
Michal Vasko428087d2016-01-14 16:04:28 +01001191API struct nc_pollsession *
1192nc_ps_new(void)
1193{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001194 struct nc_pollsession *ps;
1195
1196 ps = calloc(1, sizeof(struct nc_pollsession));
roman3a95bb22023-10-26 11:07:17 +02001197 NC_CHECK_ERRMEM_RET(!ps, NULL);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001198 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001199 pthread_mutex_init(&ps->lock, NULL);
1200
1201 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001202}
1203
1204API void
1205nc_ps_free(struct nc_pollsession *ps)
1206{
fanchanghu3d4e7212017-08-09 09:42:30 +08001207 uint16_t i;
1208
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001209 if (!ps) {
1210 return;
1211 }
1212
Michal Vaskobe86fe32016-04-07 10:43:03 +02001213 if (ps->queue_len) {
Michal Vasko05532772021-06-03 12:12:38 +02001214 ERR(NULL, "FATAL: Freeing a pollsession structure that is currently being worked with!");
Michal Vaskobe86fe32016-04-07 10:43:03 +02001215 }
1216
fanchanghu3d4e7212017-08-09 09:42:30 +08001217 for (i = 0; i < ps->session_count; i++) {
1218 free(ps->sessions[i]);
1219 }
1220
Michal Vasko428087d2016-01-14 16:04:28 +01001221 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001222 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001223 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001224
Michal Vasko428087d2016-01-14 16:04:28 +01001225 free(ps);
1226}
1227
1228API int
1229nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1230{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001231 uint8_t q_id;
1232
romanf578cd52023-10-19 09:47:40 +02001233 NC_CHECK_ARG_RET(session, ps, session, -1);
Michal Vasko428087d2016-01-14 16:04:28 +01001234
Michal Vasko48a63ed2016-03-01 09:48:21 +01001235 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001236 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001237 return -1;
1238 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001239
Michal Vasko428087d2016-01-14 16:04:28 +01001240 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001241 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001242 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001243 ERRMEM;
1244 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001245 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001246 return -1;
1247 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001248 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1249 if (!ps->sessions[ps->session_count - 1]) {
1250 ERRMEM;
1251 --ps->session_count;
1252 /* UNLOCK */
1253 nc_ps_unlock(ps, q_id, __func__);
1254 return -1;
1255 }
1256 ps->sessions[ps->session_count - 1]->session = session;
1257 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001258
Michal Vasko48a63ed2016-03-01 09:48:21 +01001259 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001260 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001261}
1262
Michal Vasko48a63ed2016-03-01 09:48:21 +01001263static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001264_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001265{
1266 uint16_t i;
1267
Radek Krejcid5f978f2016-03-03 13:14:45 +01001268 if (index >= 0) {
1269 i = (uint16_t)index;
1270 goto remove;
1271 }
Michal Vasko428087d2016-01-14 16:04:28 +01001272 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001273 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001274remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001275 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001276 if (i <= ps->session_count) {
1277 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001278 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001279 }
1280 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001281 free(ps->sessions);
1282 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001283 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001284 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001285 return 0;
1286 }
1287 }
1288
Michal Vaskof0537d82016-01-29 14:42:38 +01001289 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001290}
1291
Michal Vasko48a63ed2016-03-01 09:48:21 +01001292API int
1293nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1294{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001295 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001296 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001297
romanf578cd52023-10-19 09:47:40 +02001298 NC_CHECK_ARG_RET(session, ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001299
1300 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001301 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001302 return -1;
1303 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001304
Radek Krejcid5f978f2016-03-03 13:14:45 +01001305 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001306
1307 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001308 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001309
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001310 return ret || ret2 ? -1 : 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001311}
1312
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001313API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001314nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001315{
1316 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001317 struct nc_session *ret = NULL;
1318
romanf578cd52023-10-19 09:47:40 +02001319 NC_CHECK_ARG_RET(NULL, ps, NULL);
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001320
1321 /* LOCK */
1322 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1323 return NULL;
1324 }
1325
Michal Vasko4871c9d2017-10-09 14:48:39 +02001326 if (idx < ps->session_count) {
1327 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001328 }
1329
1330 /* UNLOCK */
1331 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1332
1333 return ret;
1334}
1335
Michal Vasko3ec3b112022-07-21 12:32:33 +02001336API struct nc_session *
1337nc_ps_find_session(const struct nc_pollsession *ps, nc_ps_session_match_cb match_cb, void *cb_data)
1338{
1339 uint8_t q_id;
1340 uint16_t i;
1341 struct nc_session *ret = NULL;
1342
romanf578cd52023-10-19 09:47:40 +02001343 NC_CHECK_ARG_RET(NULL, ps, NULL);
Michal Vasko3ec3b112022-07-21 12:32:33 +02001344
1345 /* LOCK */
1346 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1347 return NULL;
1348 }
1349
1350 for (i = 0; i < ps->session_count; ++i) {
1351 if (match_cb(ps->sessions[i]->session, cb_data)) {
1352 ret = ps->sessions[i]->session;
1353 break;
1354 }
1355 }
1356
1357 /* UNLOCK */
1358 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1359
1360 return ret;
1361}
1362
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001363API uint16_t
1364nc_ps_session_count(struct nc_pollsession *ps)
1365{
Michal Vasko47003942019-03-14 12:25:23 +01001366 uint8_t q_id;
1367 uint16_t session_count;
1368
romanf578cd52023-10-19 09:47:40 +02001369 NC_CHECK_ARG_RET(NULL, ps, 0);
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001370
Michal Vasko47003942019-03-14 12:25:23 +01001371 /* LOCK (just for memory barrier so that we read the current value) */
1372 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1373 return 0;
1374 }
1375
1376 session_count = ps->session_count;
1377
1378 /* UNLOCK */
1379 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1380
1381 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001382}
1383
Michal Vasko77e83572022-07-21 15:31:15 +02001384static NC_MSG_TYPE
1385recv_rpc_check_msgid(struct nc_session *session, const struct lyd_node *envp)
1386{
1387 struct lyd_attr *attr;
1388
1389 assert(envp && !envp->schema);
1390
1391 /* find the message-id attribute */
1392 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1393 if (!strcmp(attr->name.name, "message-id")) {
1394 break;
1395 }
1396 }
1397
1398 if (!attr) {
1399 ERR(session, "Received an <rpc> without a message-id.");
1400 return NC_MSG_REPLY_ERR_MSGID;
1401 }
1402
1403 return NC_MSG_RPC;
1404}
1405
Michal Vasko131120a2018-05-29 15:44:02 +02001406/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001407 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001408 * NC_PSPOLL_TIMEOUT,
Michal Vaskof8fba542023-10-23 12:03:50 +02001409 * NC_PSPOLL_BAD_RPC (| NC_PSPOLL_REPLY_ERROR),
Michal Vasko71090fc2016-05-24 16:37:28 +02001410 * NC_PSPOLL_RPC
1411 */
1412static int
Michal Vasko131120a2018-05-29 15:44:02 +02001413nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001414{
Michal Vasko77367452021-02-16 16:32:18 +01001415 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001416 struct nc_server_reply *reply = NULL;
Michal Vasko939ffce2021-04-12 13:02:01 +02001417 struct lyd_node *e;
Michal Vaskof8fba542023-10-23 12:03:50 +02001418 int r, ret = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001419
romanf578cd52023-10-19 09:47:40 +02001420 NC_CHECK_ARG_RET(session, session, rpc, NC_PSPOLL_ERROR);
1421
1422 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vasko05532772021-06-03 12:12:38 +02001423 ERR(session, "Invalid session to receive RPCs.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001424 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001425 }
1426
Michal Vasko93224072021-11-09 12:14:28 +01001427 *rpc = NULL;
1428
Michal Vasko77367452021-02-16 16:32:18 +01001429 /* get a message */
1430 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1431 if (r == -2) {
1432 /* malformed message */
Michal Vasko93224072021-11-09 12:14:28 +01001433 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77e83572022-07-21 15:31:15 +02001434 goto cleanup;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001435 }
1436 if (r == -1) {
Michal Vasko77367452021-02-16 16:32:18 +01001437 return NC_PSPOLL_ERROR;
1438 } else if (!r) {
1439 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001440 }
1441
Michal Vasko77367452021-02-16 16:32:18 +01001442 *rpc = calloc(1, sizeof **rpc);
roman3a95bb22023-10-26 11:07:17 +02001443 NC_CHECK_ERRMEM_GOTO(!*rpc, ret = NC_PSPOLL_ERROR, cleanup);
Michal Vasko77367452021-02-16 16:32:18 +01001444
1445 /* parse the RPC */
Michal Vasko77e83572022-07-21 15:31:15 +02001446 if (!lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_RPC_NETCONF, &(*rpc)->envp, &(*rpc)->rpc)) {
1447 /* check message-id */
1448 if (recv_rpc_check_msgid(session, (*rpc)->envp) == NC_MSG_RPC) {
1449 /* valid RPC */
1450 ret = NC_PSPOLL_RPC;
1451 } else {
1452 /* no message-id */
Michal Vasko77e83572022-07-21 15:31:15 +02001453 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MISSING_ATTR, NC_ERR_TYPE_RPC, "message-id", "rpc"));
1454 }
1455 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001456 /* bad RPC received */
Michal Vasko77367452021-02-16 16:32:18 +01001457 if ((*rpc)->envp) {
1458 /* at least the envelopes were parsed */
Michal Vasko93224072021-11-09 12:14:28 +01001459 e = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
1460 nc_err_set_msg(e, ly_errmsg(session->ctx), "en");
Michal Vasko939ffce2021-04-12 13:02:01 +02001461 reply = nc_server_reply_err(e);
Michal Vasko77367452021-02-16 16:32:18 +01001462 } else if (session->version == NC_VERSION_11) {
Michal Vasko93224072021-11-09 12:14:28 +01001463 /* completely malformed message, NETCONF version 1.1 defines sending error reply from
1464 * the server (RFC 6241 sec. 3) */
1465 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vaskof8fba542023-10-23 12:03:50 +02001466 } else {
1467 /* at least set the return value */
1468 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko77367452021-02-16 16:32:18 +01001469 }
Michal Vasko77367452021-02-16 16:32:18 +01001470 }
1471
1472cleanup:
Michal Vasko77e83572022-07-21 15:31:15 +02001473 if (reply) {
1474 /* send error reply */
1475 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, *rpc ? (*rpc)->envp : NULL, reply);
1476 nc_server_reply_free(reply);
1477 if (r != NC_MSG_REPLY) {
1478 ERR(session, "Failed to write reply (%s), terminating session.", nc_msgtype2str[r]);
1479 if (session->status != NC_STATUS_INVALID) {
1480 session->status = NC_STATUS_INVALID;
1481 session->term_reason = NC_SESSION_TERM_OTHER;
1482 }
1483 }
Michal Vaskof8fba542023-10-23 12:03:50 +02001484
1485 /* bad RPC and an error reply sent */
1486 ret = NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR;
Michal Vasko77e83572022-07-21 15:31:15 +02001487 }
1488
Michal Vasko77367452021-02-16 16:32:18 +01001489 ly_in_free(msg, 1);
1490 if (ret != NC_PSPOLL_RPC) {
1491 nc_server_rpc_free(*rpc);
1492 *rpc = NULL;
1493 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001494 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001495}
1496
fanchanghu966f2de2016-07-21 02:28:57 -04001497API void
1498nc_set_global_rpc_clb(nc_rpc_clb clb)
1499{
1500 global_rpc_clb = clb;
1501}
1502
Radek Krejci93e80222016-10-03 13:34:25 +02001503API NC_MSG_TYPE
1504nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1505{
Michal Vasko131120a2018-05-29 15:44:02 +02001506 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001507
1508 /* check parameters */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001509 if (!session || (session->side != NC_SERVER) || !nc_session_get_notif_status(session)) {
romanf578cd52023-10-19 09:47:40 +02001510 ERRARG(NULL, "session");
Radek Krejci93e80222016-10-03 13:34:25 +02001511 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001512 } else if (!notif || !notif->ntf || !notif->eventtime) {
romanf578cd52023-10-19 09:47:40 +02001513 ERRARG(NULL, "notif");
Radek Krejci93e80222016-10-03 13:34:25 +02001514 return NC_MSG_ERROR;
1515 }
1516
Michal Vasko131120a2018-05-29 15:44:02 +02001517 /* we do not need RPC lock for this, IO lock will be acquired properly */
1518 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001519 if (ret != NC_MSG_NOTIF) {
Michal Vasko05532772021-06-03 12:12:38 +02001520 ERR(session, "Failed to write notification (%s).", nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001521 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001522
Michal Vasko131120a2018-05-29 15:44:02 +02001523 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001524}
1525
Michal Vaskof9467762023-03-28 09:02:08 +02001526/**
1527 * @brief Send a reply acquiring IO lock as needed.
1528 * Session RPC lock must be held!
1529 *
1530 * @param[in] session Session to use.
1531 * @param[in] io_timeout Timeout to use for acquiring IO lock.
1532 * @param[in] rpc RPC to sent.
1533 * @return 0 on success.
1534 * @return Bitmask of NC_PSPOLL_ERROR (any fatal error) and NC_PSPOLL_REPLY_ERROR (reply failed to be sent).
1535 * @return NC_PSPOLL_ERROR on other errors.
Michal Vasko71090fc2016-05-24 16:37:28 +02001536 */
1537static int
Michal Vasko93224072021-11-09 12:14:28 +01001538nc_server_send_reply_io(struct nc_session *session, int io_timeout, const struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001539{
1540 nc_rpc_clb clb;
1541 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001542 const struct lysc_node *rpc_act = NULL;
1543 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001544 int ret = 0;
1545 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001546
Michal Vasko4a827e52016-03-03 10:59:00 +01001547 if (!rpc) {
1548 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001549 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001550 }
1551
Michal Vasko77367452021-02-16 16:32:18 +01001552 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001553 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001554 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001555 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001556 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001557 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001558 if (elem->schema->nodetype == LYS_ACTION) {
1559 rpc_act = elem->schema;
1560 break;
1561 }
Michal Vasko77367452021-02-16 16:32:18 +01001562 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001563 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001564 if (!rpc_act) {
1565 ERRINT;
1566 return NC_PSPOLL_ERROR;
1567 }
1568 }
1569
1570 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001571 if (!global_rpc_clb) {
1572 /* no callback, reply with a not-implemented error */
Michal Vasko93224072021-11-09 12:14:28 +01001573 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 +03001574 } else {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001575 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001576 }
Michal Vasko428087d2016-01-14 16:04:28 +01001577 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001578 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001579 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001580 }
1581
1582 if (!reply) {
Michal Vasko93224072021-11-09 12:14:28 +01001583 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001584 }
Michal Vasko77367452021-02-16 16:32:18 +01001585 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001586 if (reply->type == NC_RPL_ERROR) {
1587 ret |= NC_PSPOLL_REPLY_ERROR;
1588 }
1589 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001590
Michal Vasko131120a2018-05-29 15:44:02 +02001591 if (r != NC_MSG_REPLY) {
Michal Vasko15469492021-06-09 08:40:48 +02001592 ERR(session, "Failed to write reply (%s).", nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001593 ret |= NC_PSPOLL_ERROR;
1594 }
Michal Vasko428087d2016-01-14 16:04:28 +01001595
1596 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1597 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1598 session->status = NC_STATUS_INVALID;
1599 }
1600
Michal Vasko71090fc2016-05-24 16:37:28 +02001601 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001602}
1603
Michal Vaskof9467762023-03-28 09:02:08 +02001604/**
1605 * @brief Poll a session from pspoll acquiring IO lock as needed.
1606 * Session must be running and session RPC lock held!
1607 *
1608 * @param[in] session Session to use.
1609 * @param[in] io_timeout Timeout to use for acquiring IO lock.
1610 * @param[in] now_mono Current monotonic timestamp.
1611 * @param[in,out] msg Message to fill in case of an error.
1612 * @return NC_PSPOLL_RPC if some application data are available.
1613 * @return NC_PSPOLL_TIMEOUT if a timeout elapsed.
1614 * @return NC_PSPOLL_SSH_CHANNEL if a new SSH channel has been created.
1615 * @return NC_PSPOLL_SSH_MSG if just an SSH message has been processed.
1616 * @return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR if session has been terminated (@p msg filled).
1617 * @return NC_PSPOLL_ERROR on other fatal errors (@p msg filled).
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001618 */
1619static int
Michal Vasko131120a2018-05-29 15:44:02 +02001620nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001621{
Michal Vasko9a327362017-01-11 11:31:46 +01001622 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001623 int r, ret = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001624
romanf578cd52023-10-19 09:47:40 +02001625#ifdef NC_ENABLED_SSH_TLS
roman456f92d2023-04-28 10:28:12 +02001626 ssh_message ssh_msg;
Michal Vasko9a327362017-01-11 11:31:46 +01001627 struct nc_session *new;
romanf578cd52023-10-19 09:47:40 +02001628#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko428087d2016-01-14 16:04:28 +01001629
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001630 /* check timeout first */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001631 if (!(session->flags & NC_SESSION_CALLHOME) && !nc_session_get_notif_status(session) && server_opts.idle_timeout &&
romanf578cd52023-10-19 09:47:40 +02001632 (now_mono >= session->opts.server.last_rpc + (unsigned) server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001633 sprintf(msg, "session idle timeout elapsed");
1634 session->status = NC_STATUS_INVALID;
1635 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1636 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1637 }
1638
Michal Vasko131120a2018-05-29 15:44:02 +02001639 r = nc_session_io_lock(session, io_timeout, __func__);
1640 if (r < 0) {
1641 sprintf(msg, "session IO lock failed to be acquired");
1642 return NC_PSPOLL_ERROR;
1643 } else if (!r) {
1644 return NC_PSPOLL_TIMEOUT;
1645 }
1646
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001647 switch (session->ti_type) {
romanf578cd52023-10-19 09:47:40 +02001648#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001649 case NC_TI_LIBSSH:
romanf578cd52023-10-19 09:47:40 +02001650 ssh_msg = ssh_message_get(session->ti.libssh.session);
1651 if (ssh_msg) {
1652 nc_session_ssh_msg(session, NULL, ssh_msg, NULL);
1653 if (session->ti.libssh.next) {
1654 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1655 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
1656 (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1657 /* new NETCONF SSH channel */
1658 ret = NC_PSPOLL_SSH_CHANNEL;
1659 break;
1660 }
1661 }
1662 if (new != session) {
1663 ssh_message_free(ssh_msg);
1664 break;
1665 }
1666 }
1667 if (!ret) {
1668 /* just some SSH message */
1669 ret = NC_PSPOLL_SSH_MSG;
1670 }
1671 ssh_message_free(ssh_msg);
1672
1673 /* break because 1) we don't want to return anything here ORred with NC_PSPOLL_RPC
1674 * and 2) we don't want to delay openning a new channel by waiting for a RPC to get processed
1675 */
1676 break;
1677 }
1678
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001679 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001680 if (r == SSH_EOF) {
1681 sprintf(msg, "SSH channel unexpected EOF");
1682 session->status = NC_STATUS_INVALID;
1683 session->term_reason = NC_SESSION_TERM_DROPPED;
1684 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1685 } else if (r == SSH_ERROR) {
1686 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001687 session->status = NC_STATUS_INVALID;
1688 session->term_reason = NC_SESSION_TERM_OTHER;
1689 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001690 } else if (!r) {
romanf578cd52023-10-19 09:47:40 +02001691 /* no application data received */
1692 ret = NC_PSPOLL_TIMEOUT;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001693 } else {
1694 /* we have some application data */
1695 ret = NC_PSPOLL_RPC;
1696 }
1697 break;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001698 case NC_TI_OPENSSL:
1699 r = SSL_pending(session->ti.tls);
1700 if (!r) {
1701 /* no data pending in the SSL buffer, poll fd */
1702 pfd.fd = SSL_get_rfd(session->ti.tls);
1703 if (pfd.fd < 0) {
1704 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1705 ret = NC_PSPOLL_ERROR;
1706 break;
1707 }
1708 pfd.events = POLLIN;
1709 pfd.revents = 0;
1710 r = poll(&pfd, 1, 0);
1711
1712 if ((r < 0) && (errno != EINTR)) {
1713 sprintf(msg, "poll failed (%s)", strerror(errno));
1714 session->status = NC_STATUS_INVALID;
1715 ret = NC_PSPOLL_ERROR;
1716 } else if (r > 0) {
1717 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1718 sprintf(msg, "communication socket unexpectedly closed");
1719 session->status = NC_STATUS_INVALID;
1720 session->term_reason = NC_SESSION_TERM_DROPPED;
1721 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1722 } else if (pfd.revents & POLLERR) {
1723 sprintf(msg, "communication socket error");
1724 session->status = NC_STATUS_INVALID;
1725 session->term_reason = NC_SESSION_TERM_OTHER;
1726 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1727 } else {
1728 ret = NC_PSPOLL_RPC;
1729 }
1730 } else {
1731 ret = NC_PSPOLL_TIMEOUT;
1732 }
1733 } else {
1734 ret = NC_PSPOLL_RPC;
1735 }
1736 break;
romanf578cd52023-10-19 09:47:40 +02001737#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001738 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001739 case NC_TI_UNIX:
1740 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001741 pfd.events = POLLIN;
1742 pfd.revents = 0;
1743 r = poll(&pfd, 1, 0);
1744
1745 if ((r < 0) && (errno != EINTR)) {
1746 sprintf(msg, "poll failed (%s)", strerror(errno));
1747 session->status = NC_STATUS_INVALID;
1748 ret = NC_PSPOLL_ERROR;
1749 } else if (r > 0) {
1750 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1751 sprintf(msg, "communication socket unexpectedly closed");
1752 session->status = NC_STATUS_INVALID;
1753 session->term_reason = NC_SESSION_TERM_DROPPED;
1754 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1755 } else if (pfd.revents & POLLERR) {
1756 sprintf(msg, "communication socket error");
1757 session->status = NC_STATUS_INVALID;
1758 session->term_reason = NC_SESSION_TERM_OTHER;
1759 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1760 } else {
1761 ret = NC_PSPOLL_RPC;
1762 }
1763 } else {
1764 ret = NC_PSPOLL_TIMEOUT;
1765 }
1766 break;
1767 case NC_TI_NONE:
1768 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1769 ret = NC_PSPOLL_ERROR;
1770 break;
1771 }
1772
Michal Vasko131120a2018-05-29 15:44:02 +02001773 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001774 return ret;
1775}
1776
1777API int
1778nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1779{
Michal Vasko443faa02022-10-20 09:09:03 +02001780 int ret = NC_PSPOLL_ERROR, r;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001781 uint8_t q_id;
1782 uint16_t i, j;
1783 char msg[256];
1784 struct timespec ts_timeout, ts_cur;
1785 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001786 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001787 struct nc_server_rpc *rpc = NULL;
1788
romanf578cd52023-10-19 09:47:40 +02001789 NC_CHECK_ARG_RET(NULL, ps, NC_PSPOLL_ERROR);
Michal Vasko428087d2016-01-14 16:04:28 +01001790
Michal Vaskoade892d2017-02-22 13:40:35 +01001791 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001792 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001793 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001794 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001795
Michal Vaskoade892d2017-02-22 13:40:35 +01001796 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001797 nc_ps_unlock(ps, q_id, __func__);
1798 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001799 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001800
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001801 /* fill timespecs */
Michal Vaskod8a74192023-02-06 15:51:50 +01001802 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko36c7be82017-02-22 13:37:59 +01001803 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001804 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001805 }
1806
1807 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001808 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001809 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001810 if (ps->last_event_session == ps->session_count - 1) {
1811 i = j = 0;
1812 } else {
1813 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001814 }
Michal Vasko9a327362017-01-11 11:31:46 +01001815 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001816 cur_ps_session = ps->sessions[i];
1817 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001818
Michal Vasko131120a2018-05-29 15:44:02 +02001819 /* SESSION RPC LOCK */
1820 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001821 if (r == -1) {
1822 ret = NC_PSPOLL_ERROR;
1823 } else if (r == 1) {
1824 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001825 switch (cur_ps_session->state) {
1826 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001827 if (cur_session->status == NC_STATUS_RUNNING) {
1828 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001829 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001830
Michal Vasko131120a2018-05-29 15:44:02 +02001831 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001832 switch (ret) {
1833 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001834 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001835 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001836 break;
1837 case NC_PSPOLL_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001838 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001839 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001840 break;
1841 case NC_PSPOLL_TIMEOUT:
romanf578cd52023-10-19 09:47:40 +02001842#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001843 case NC_PSPOLL_SSH_CHANNEL:
1844 case NC_PSPOLL_SSH_MSG:
romanf578cd52023-10-19 09:47:40 +02001845#endif /* NC_ENABLED_SSH_TLS */
fanchanghu3d4e7212017-08-09 09:42:30 +08001846 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001847 break;
1848 case NC_PSPOLL_RPC:
1849 /* let's keep the state busy, we are not done with this session */
1850 break;
1851 }
1852 } else {
1853 /* session is not fine, let the caller know */
1854 ret = NC_PSPOLL_SESSION_TERM;
1855 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1856 ret |= NC_PSPOLL_SESSION_ERROR;
1857 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001858 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001859 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001860 break;
1861 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001862 /* it definitely should not be busy because we have the lock */
1863 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001864 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001865 break;
1866 case NC_PS_STATE_INVALID:
1867 /* we got it locked, but it will be freed, let it be */
1868 ret = NC_PSPOLL_TIMEOUT;
1869 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001870 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001871
Michal Vasko131120a2018-05-29 15:44:02 +02001872 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001873 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001874 /* SESSION RPC UNLOCK */
1875 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001876 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001877 } else {
1878 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001879 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001880 }
Michal Vasko428087d2016-01-14 16:04:28 +01001881
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001882 /* something happened */
1883 if (ret != NC_PSPOLL_TIMEOUT) {
1884 break;
1885 }
1886
Michal Vasko9a327362017-01-11 11:31:46 +01001887 if (i == ps->session_count - 1) {
1888 i = 0;
1889 } else {
1890 ++i;
1891 }
1892 } while (i != j);
1893
Michal Vaskoade892d2017-02-22 13:40:35 +01001894 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001895 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001896 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001897
Michal Vaskod8a74192023-02-06 15:51:50 +01001898 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001899 /* final timeout */
1900 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001901 }
Michal Vasko428087d2016-01-14 16:04:28 +01001902 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001903 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001904
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001905 /* do we want to return the session? */
1906 switch (ret) {
1907 case NC_PSPOLL_RPC:
1908 case NC_PSPOLL_SESSION_TERM:
1909 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
romanf578cd52023-10-19 09:47:40 +02001910#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001911 case NC_PSPOLL_SSH_CHANNEL:
1912 case NC_PSPOLL_SSH_MSG:
romanf578cd52023-10-19 09:47:40 +02001913#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001914 if (session) {
1915 *session = cur_session;
1916 }
1917 ps->last_event_session = i;
1918 break;
1919 default:
1920 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001921 }
Michal Vasko428087d2016-01-14 16:04:28 +01001922
Michal Vaskoade892d2017-02-22 13:40:35 +01001923 /* PS UNLOCK */
1924 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001925
Michal Vasko131120a2018-05-29 15:44:02 +02001926 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001927 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001928 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001929 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1930 if (cur_session->status != NC_STATUS_RUNNING) {
1931 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001932 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001933 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001934 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001935 }
1936 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001937 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001938
Michal Vasko7f1ee932018-10-11 09:41:42 +02001939 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001940 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001941 if (cur_session->status != NC_STATUS_RUNNING) {
1942 ret |= NC_PSPOLL_SESSION_TERM;
1943 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1944 ret |= NC_PSPOLL_SESSION_ERROR;
1945 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001946 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001947 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001948 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001949 }
Michal Vasko428087d2016-01-14 16:04:28 +01001950 }
Michal Vasko77367452021-02-16 16:32:18 +01001951 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001952
Michal Vasko131120a2018-05-29 15:44:02 +02001953 /* SESSION RPC UNLOCK */
1954 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001955 }
1956
Michal Vasko48a63ed2016-03-01 09:48:21 +01001957 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001958}
1959
Michal Vaskod09eae62016-02-01 10:32:52 +01001960API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001961nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001962{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001963 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001964 uint16_t i;
1965 struct nc_session *session;
1966
Michal Vasko9a25e932016-02-01 10:36:42 +01001967 if (!ps) {
romanf578cd52023-10-19 09:47:40 +02001968 ERRARG(NULL, "ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001969 return;
1970 }
1971
Michal Vasko48a63ed2016-03-01 09:48:21 +01001972 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001973 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001974 return;
1975 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001976
Michal Vasko48a63ed2016-03-01 09:48:21 +01001977 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001978 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001979 nc_session_free(ps->sessions[i]->session, data_free);
1980 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001981 }
1982 free(ps->sessions);
1983 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001984 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001985 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001986 } else {
1987 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001988 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1989 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001990 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001991 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001992 continue;
1993 }
1994
1995 ++i;
1996 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001997 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001998
1999 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02002000 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01002001}
2002
Michal Vasko5f352c52019-07-10 16:12:06 +02002003static int
apropp-molex4e903c32020-04-20 03:06:58 -04002004nc_get_uid(int sock, uid_t *uid)
2005{
Michal Vaskod3910912020-04-20 09:12:49 +02002006 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04002007
Michal Vaskod3910912020-04-20 09:12:49 +02002008#ifdef SO_PEERCRED
2009 struct ucred ucred;
2010 socklen_t len;
Michal Vasko292c5542023-02-01 14:33:17 +01002011
Michal Vaskod3910912020-04-20 09:12:49 +02002012 len = sizeof(ucred);
2013 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
2014 if (!ret) {
2015 *uid = ucred.uid;
2016 }
2017#else
2018 ret = getpeereid(sock, uid, NULL);
2019#endif
2020
2021 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02002022 ERR(NULL, "Failed to get credentials from unix socket (%s).", strerror(errno));
Michal Vaskod3910912020-04-20 09:12:49 +02002023 return -1;
2024 }
apropp-molex4e903c32020-04-20 03:06:58 -04002025 return 0;
2026}
2027
2028static int
Michal Vasko5f352c52019-07-10 16:12:06 +02002029nc_accept_unix(struct nc_session *session, int sock)
2030{
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002031#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02002032 struct passwd *pw, pw_buf;
Michal Vasko5f352c52019-07-10 16:12:06 +02002033 char *username;
Michal Vasko292c5542023-02-01 14:33:17 +01002034
Michal Vasko5f352c52019-07-10 16:12:06 +02002035 session->ti_type = NC_TI_UNIX;
Michal Vasko143aa142021-10-01 15:31:48 +02002036 uid_t uid = 0;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02002037 char *buf = NULL;
2038 size_t buf_len = 0;
Michal Vasko5f352c52019-07-10 16:12:06 +02002039
Michal Vaskod3910912020-04-20 09:12:49 +02002040 if (nc_get_uid(sock, &uid)) {
2041 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02002042 return -1;
2043 }
2044
romanf6e32012023-04-24 15:51:26 +02002045 pw = nc_getpw(uid, NULL, &pw_buf, &buf, &buf_len);
Michal Vasko5f352c52019-07-10 16:12:06 +02002046 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02002047 ERR(NULL, "Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02002048 close(sock);
2049 return -1;
2050 }
2051
2052 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02002053 free(buf);
Michal Vasko5f352c52019-07-10 16:12:06 +02002054 if (username == NULL) {
2055 ERRMEM;
2056 close(sock);
2057 return -1;
2058 }
Michal Vasko93224072021-11-09 12:14:28 +01002059 session->username = username;
Michal Vasko5f352c52019-07-10 16:12:06 +02002060
2061 session->ti.unixsock.sock = sock;
2062
2063 return 1;
Claus Klein22091912020-01-20 13:45:47 +01002064#else
2065 return -1;
2066#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02002067}
2068
Michal Vaskoe2713da2016-08-22 16:06:40 +02002069API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002070nc_server_endpt_count(void)
2071{
2072 return server_opts.endpt_count;
2073}
2074
Michal Vasko1b5973e2020-01-30 16:05:46 +01002075API int
2076nc_server_is_endpt(const char *name)
2077{
2078 uint16_t i;
2079 int found = 0;
2080
Michal Vaskofb1724b2020-01-31 11:02:00 +01002081 if (!name) {
2082 return found;
2083 }
2084
romanf578cd52023-10-19 09:47:40 +02002085 /* CONFIG READ LOCK */
2086 pthread_rwlock_rdlock(&server_opts.config_lock);
Michal Vasko1b5973e2020-01-30 16:05:46 +01002087
2088 /* check name uniqueness */
2089 for (i = 0; i < server_opts.endpt_count; ++i) {
2090 if (!strcmp(server_opts.endpts[i].name, name)) {
2091 found = 1;
2092 break;
2093 }
2094 }
2095
romanf578cd52023-10-19 09:47:40 +02002096 /* CONFIG UNLOCK */
2097 pthread_rwlock_unlock(&server_opts.config_lock);
Michal Vasko1b5973e2020-01-30 16:05:46 +01002098
2099 return found;
2100}
2101
Michal Vasko71090fc2016-05-24 16:37:28 +02002102API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +01002103nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002104{
Michal Vasko71090fc2016-05-24 16:37:28 +02002105 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002106 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002107 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002108 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002109 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002110
romanf578cd52023-10-19 09:47:40 +02002111 NC_CHECK_ARG_RET(NULL, ctx, session, NC_MSG_ERROR);
Michal Vasko9e036d52016-01-08 10:49:26 +01002112
Michal Vasko93224072021-11-09 12:14:28 +01002113 /* init ctx as needed */
romanf578cd52023-10-19 09:47:40 +02002114 nc_server_init_cb_ctx(ctx);
Michal Vasko93224072021-11-09 12:14:28 +01002115
romanf578cd52023-10-19 09:47:40 +02002116 /* CONFIG LOCK */
2117 pthread_rwlock_rdlock(&server_opts.config_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002118
2119 if (!server_opts.endpt_count) {
Michal Vasko05532772021-06-03 12:12:38 +02002120 ERR(NULL, "No endpoints to accept sessions on.");
romanf578cd52023-10-19 09:47:40 +02002121 /* CONFIG UNLOCK */
2122 pthread_rwlock_unlock(&server_opts.config_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002123 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002124 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002125
romanf578cd52023-10-19 09:47:40 +02002126 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 +01002127 if (ret < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01002128 free(host);
romanf578cd52023-10-19 09:47:40 +02002129 /* CONFIG UNLOCK */
2130 pthread_rwlock_unlock(&server_opts.config_lock);
Michal Vasko5e203472016-05-30 15:27:58 +02002131 if (!ret) {
2132 return NC_MSG_WOULDBLOCK;
2133 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002134 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002135 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002136
Michal Vaskob48aa812016-01-18 14:13:09 +01002137 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002138
Michal Vasko131120a2018-05-29 15:44:02 +02002139 *session = nc_new_session(NC_SERVER, 0);
roman3a95bb22023-10-26 11:07:17 +02002140 NC_CHECK_ERRMEM_GOTO(!(*session), close(sock); free(host); msgtype = NC_MSG_ERROR, cleanup);
Michal Vasko1a38c862016-01-15 15:50:07 +01002141 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01002142 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko1a38c862016-01-15 15:50:07 +01002143 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01002144 (*session)->host = host;
Michal Vasko1a38c862016-01-15 15:50:07 +01002145 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002146
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002147 /* sock gets assigned to session or closed */
romanf578cd52023-10-19 09:47:40 +02002148#ifdef NC_ENABLED_SSH_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002149 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
romanf578cd52023-10-19 09:47:40 +02002150 ret = nc_accept_ssh_session(*session, server_opts.endpts[bind_idx].opts.ssh, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002151 if (ret < 0) {
2152 msgtype = NC_MSG_ERROR;
2153 goto cleanup;
2154 } else if (!ret) {
2155 msgtype = NC_MSG_WOULDBLOCK;
2156 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002157 }
romanf578cd52023-10-19 09:47:40 +02002158 } else if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002159 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
romanf578cd52023-10-19 09:47:40 +02002160 ret = nc_accept_tls_session(*session, server_opts.endpts[bind_idx].opts.tls, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002161 if (ret < 0) {
2162 msgtype = NC_MSG_ERROR;
2163 goto cleanup;
2164 } else if (!ret) {
2165 msgtype = NC_MSG_WOULDBLOCK;
2166 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002167 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002168 } else
romanf578cd52023-10-19 09:47:40 +02002169#endif /* NC_ENABLED_SSH_TLS */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002170 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2171 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2172 ret = nc_accept_unix(*session, sock);
2173 if (ret < 0) {
2174 msgtype = NC_MSG_ERROR;
2175 goto cleanup;
2176 }
2177 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002178 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002179 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002180 msgtype = NC_MSG_ERROR;
2181 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002182 }
2183
Michal Vasko2cc4c682016-03-01 09:16:48 +01002184 (*session)->data = NULL;
2185
romanf578cd52023-10-19 09:47:40 +02002186 /* CONFIG UNLOCK */
2187 pthread_rwlock_unlock(&server_opts.config_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002188
Michal Vaskob48aa812016-01-18 14:13:09 +01002189 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002190 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002191
Michal Vasko9e036d52016-01-08 10:49:26 +01002192 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002193 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002194 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002195 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002196 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002197 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002198 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002199
Michal Vaskod8a74192023-02-06 15:51:50 +01002200 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02002201 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +01002202 nc_realtime_get(&ts_cur);
Michal Vasko9fb42272017-10-05 13:50:05 +02002203 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002204 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002205
Michal Vasko71090fc2016-05-24 16:37:28 +02002206 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002207
Michal Vasko71090fc2016-05-24 16:37:28 +02002208cleanup:
romanf578cd52023-10-19 09:47:40 +02002209 /* CONFIG UNLOCK */
2210 pthread_rwlock_unlock(&server_opts.config_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002211
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002212 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002213 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002214 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002215}
2216
romanf578cd52023-10-19 09:47:40 +02002217#ifdef NC_ENABLED_SSH_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002218
2219API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002220nc_server_ch_is_client(const char *name)
2221{
2222 uint16_t i;
2223 int found = 0;
2224
2225 if (!name) {
2226 return found;
2227 }
2228
2229 /* READ LOCK */
2230 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2231
2232 /* check name uniqueness */
2233 for (i = 0; i < server_opts.ch_client_count; ++i) {
2234 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2235 found = 1;
2236 break;
2237 }
2238 }
2239
2240 /* UNLOCK */
2241 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2242
2243 return found;
2244}
2245
2246API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002247nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2248{
2249 uint16_t i;
2250 struct nc_ch_client *client = NULL;
2251 int found = 0;
2252
2253 if (!client_name || !endpt_name) {
2254 return found;
2255 }
2256
2257 /* READ LOCK */
2258 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2259
2260 for (i = 0; i < server_opts.ch_client_count; ++i) {
2261 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2262 client = &server_opts.ch_clients[i];
2263 break;
2264 }
2265 }
2266
2267 if (!client) {
2268 goto cleanup;
2269 }
2270
2271 for (i = 0; i < client->ch_endpt_count; ++i) {
2272 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2273 found = 1;
2274 break;
2275 }
2276 }
2277
2278cleanup:
2279 /* UNLOCK */
2280 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2281 return found;
2282}
2283
Michal Vasko056f53c2022-10-21 13:38:15 +02002284/**
2285 * @brief Create a connection for an endpoint.
2286 *
2287 * Client lock is expected to be held.
2288 *
2289 * @param[in] endpt Endpoint to use.
2290 * @param[in] acquire_ctx_cb Callback for acquiring the libyang context.
2291 * @param[in] release_ctx_cb Callback for releasing the libyang context.
2292 * @param[in] ctx_cb_data Context callbacks data.
2293 * @param[out] session Created NC session.
2294 * @return NC_MSG values.
2295 */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002296static NC_MSG_TYPE
Michal Vasko58bac1c2022-03-24 15:25:26 +01002297nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
2298 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 +01002299{
Michal Vasko71090fc2016-05-24 16:37:28 +02002300 NC_MSG_TYPE msgtype;
Michal Vasko58bac1c2022-03-24 15:25:26 +01002301 const struct ly_ctx *ctx = NULL;
Michal Vaskob05053d2016-01-22 16:12:06 +01002302 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002303 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01002304 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01002305
Michal Vasko056f53c2022-10-21 13:38:15 +02002306 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 +01002307 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002308 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002309 }
2310
Michal Vasko93224072021-11-09 12:14:28 +01002311 /* acquire context */
2312 ctx = acquire_ctx_cb(ctx_cb_data);
2313 if (!ctx) {
2314 ERR(NULL, "Failed to acquire context for a new Call Home session.");
2315 close(sock);
2316 free(ip_host);
2317 return NC_MSG_ERROR;
2318 }
2319
romanf578cd52023-10-19 09:47:40 +02002320 /* init ctx as needed */
2321 nc_server_init_cb_ctx(ctx);
2322
Michal Vasko93224072021-11-09 12:14:28 +01002323 /* create session */
Michal Vasko131120a2018-05-29 15:44:02 +02002324 *session = nc_new_session(NC_SERVER, 0);
roman3a95bb22023-10-26 11:07:17 +02002325 NC_CHECK_ERRMEM_GOTO(!(*session), close(sock); free(ip_host); msgtype = NC_MSG_ERROR, fail);
Michal Vaskob05053d2016-01-22 16:12:06 +01002326 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01002327 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vaskodc96bb92023-03-28 08:52:48 +02002328 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vasko93224072021-11-09 12:14:28 +01002329 (*session)->host = ip_host;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002330 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002331
Michal Vaskob05053d2016-01-22 16:12:06 +01002332 /* sock gets assigned to session or closed */
romanf578cd52023-10-19 09:47:40 +02002333#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02002334 if (endpt->ti == NC_TI_LIBSSH) {
romanf578cd52023-10-19 09:47:40 +02002335 ret = nc_accept_ssh_session(*session, endpt->opts.ssh, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002336 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002337
Michal Vasko71090fc2016-05-24 16:37:28 +02002338 if (ret < 0) {
2339 msgtype = NC_MSG_ERROR;
2340 goto fail;
2341 } else if (!ret) {
2342 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002343 goto fail;
2344 }
romanf578cd52023-10-19 09:47:40 +02002345 } else if (endpt->ti == NC_TI_OPENSSL) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002346 (*session)->data = endpt->opts.tls;
romanf578cd52023-10-19 09:47:40 +02002347 ret = nc_accept_tls_session(*session, endpt->opts.tls, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002348 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002349
Michal Vasko71090fc2016-05-24 16:37:28 +02002350 if (ret < 0) {
2351 msgtype = NC_MSG_ERROR;
2352 goto fail;
2353 } else if (!ret) {
2354 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002355 goto fail;
2356 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002357 } else
romanf578cd52023-10-19 09:47:40 +02002358#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01002359 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002360 ERRINT;
2361 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002362 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002363 goto fail;
2364 }
2365
2366 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002367 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01002368
2369 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002370 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002371 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002372 goto fail;
2373 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002374
Michal Vaskod8a74192023-02-06 15:51:50 +01002375 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02002376 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +01002377 nc_realtime_get(&ts_cur);
Michal Vasko9fb42272017-10-05 13:50:05 +02002378 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01002379 (*session)->status = NC_STATUS_RUNNING;
2380
Michal Vasko71090fc2016-05-24 16:37:28 +02002381 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002382
2383fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002384 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002385 *session = NULL;
Michal Vasko58bac1c2022-03-24 15:25:26 +01002386 if (ctx) {
2387 release_ctx_cb(ctx_cb_data);
2388 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002389 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002390}
2391
Michal Vasko2e6defd2016-10-07 15:48:15 +02002392static struct nc_ch_client *
2393nc_server_ch_client_with_endpt_lock(const char *name)
2394{
2395 struct nc_ch_client *client;
2396
2397 while (1) {
2398 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002399 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002400 if (!client) {
2401 return NULL;
2402 }
2403 if (client->ch_endpt_count) {
2404 return client;
2405 }
2406 /* no endpoints defined yet */
2407
2408 /* UNLOCK */
2409 nc_server_ch_client_unlock(client);
2410
2411 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2412 }
2413
2414 return NULL;
2415}
2416
2417static int
2418nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2419{
Michal Vasko3f05a092018-03-13 10:39:49 +01002420 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002421 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002422 struct timespec ts;
2423 struct nc_ch_client *client;
2424
Michal Vasko2e6defd2016-10-07 15:48:15 +02002425 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01002426 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002427
Michal Vaskofeccb312022-03-24 15:24:59 +01002428 session->flags |= NC_SESSION_CH_THREAD;
Michal Vasko0db3db52021-03-03 10:45:42 +01002429
Michal Vasko2e6defd2016-10-07 15:48:15 +02002430 /* give the session to the user */
romanf578cd52023-10-19 09:47:40 +02002431 if (data->new_session_cb(data->client_name, session, data->new_session_cb_data)) {
Michal Vaskof1c26c22021-04-12 16:34:33 +02002432 /* something is wrong, free the session */
Michal Vaskofeccb312022-03-24 15:24:59 +01002433 session->flags &= ~NC_SESSION_CH_THREAD;
Michal Vaskof1c26c22021-04-12 16:34:33 +02002434
2435 /* CH UNLOCK */
2436 pthread_mutex_unlock(&session->opts.server.ch_lock);
2437
Michal Vasko77d56d72022-09-07 10:30:48 +02002438 /* session terminated, free it and release its context */
Michal Vaskof1c26c22021-04-12 16:34:33 +02002439 nc_session_free(session, NULL);
Michal Vasko58bac1c2022-03-24 15:25:26 +01002440 data->release_ctx_cb(data->ctx_cb_data);
2441 return ret;
Michal Vaskof1c26c22021-04-12 16:34:33 +02002442 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002443
2444 do {
romanf578cd52023-10-19 09:47:40 +02002445 nc_timeouttime_get(&ts, NC_CH_THREAD_IDLE_TIMEOUT_SLEEP);
Michal Vasko0db3db52021-03-03 10:45:42 +01002446 /* CH COND WAIT */
Michal Vaskod8a74192023-02-06 15:51:50 +01002447 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 +01002448 if (!r) {
2449 /* we were woken up, something probably happened */
2450 if (session->status != NC_STATUS_RUNNING) {
2451 break;
2452 }
2453 } else if (r != ETIMEDOUT) {
Michal Vasko05532772021-06-03 12:12:38 +02002454 ERR(session, "Pthread condition timedwait failed (%s).", strerror(r));
Michal Vasko3f05a092018-03-13 10:39:49 +01002455 ret = -1;
2456 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01002457 }
2458
Michal Vasko2e6defd2016-10-07 15:48:15 +02002459 /* check whether the client was not removed */
2460 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002461 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002462 if (!client) {
2463 /* client was removed, finish thread */
Michal Vasko05532772021-06-03 12:12:38 +02002464 VRB(session, "Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02002465 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01002466 ret = 1;
2467 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002468 }
2469
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002470 if (client->conn_type == NC_CH_PERIOD) {
romanf578cd52023-10-19 09:47:40 +02002471 idle_timeout = client->idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002472 } else {
2473 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002474 }
2475
Michal Vaskod8a74192023-02-06 15:51:50 +01002476 nc_timeouttime_get(&ts, 0);
Michal Vaskodf68e7e2022-04-21 11:04:00 +02002477 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 +02002478 VRB(session, "Call Home client \"%s\": session idle timeout elapsed.", client->name);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002479 session->status = NC_STATUS_INVALID;
2480 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2481 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002482
2483 /* UNLOCK */
2484 nc_server_ch_client_unlock(client);
2485
2486 } while (session->status == NC_STATUS_RUNNING);
2487
Michal Vaskofeccb312022-03-24 15:24:59 +01002488 /* signal to nc_session_free() that CH thread is terminating */
2489 session->flags &= ~NC_SESSION_CH_THREAD;
2490 pthread_cond_signal(&session->opts.server.ch_cond);
Michal Vasko0db3db52021-03-03 10:45:42 +01002491
Michal Vasko27377422018-03-15 08:59:35 +01002492 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01002493 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01002494
Michal Vasko3f05a092018-03-13 10:39:49 +01002495 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002496}
2497
romanf578cd52023-10-19 09:47:40 +02002498/**
2499 * @brief Waits for some amount of time while reacting to signals about terminating a Call Home thread.
2500 *
2501 * @param[in] session An established session.
2502 * @param[in] data Call Home thread's data.
2503 * @param[in] cond_wait_time Time in seconds to sleep for, after which a reconnect is attempted.
2504 *
2505 * @return 0 if the thread should stop running, 1 if it should continue.
2506 */
2507static int
2508nc_server_ch_client_thread_is_running_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data, uint64_t cond_wait_time)
2509{
2510 struct timespec ts;
2511 int ret = 0, thread_running;
2512
2513 /* COND LOCK */
2514 pthread_mutex_lock(&data->cond_lock);
2515 /* get reconnect timeout in ms */
2516 nc_timeouttime_get(&ts, cond_wait_time * 1000);
2517 while (!ret && data->thread_running) {
2518 ret = pthread_cond_clockwait(&data->cond, &data->cond_lock, COMPAT_CLOCK_ID, &ts);
2519 }
2520
2521 thread_running = data->thread_running;
2522 /* COND UNLOCK */
2523 pthread_mutex_unlock(&data->cond_lock);
2524
2525 if (!thread_running) {
2526 /* thread is terminating */
2527 VRB(session, "Call Home thread signaled to exit, client \"%s\" probably removed.", data->client_name);
2528 ret = 0;
2529 } else if (ret == ETIMEDOUT) {
2530 /* time to reconnect */
2531 VRB(session, "Call Home client \"%s\" timeout of %" PRIu64 " seconds expired, reconnecting.", data->client_name, cond_wait_time);
2532 ret = 1;
2533 } else if (ret) {
2534 ERR(session, "Pthread condition timedwait failed (%s).", strerror(ret));
2535 ret = 0;
2536 }
2537
2538 return ret;
2539}
2540
2541/**
2542 * @brief Checks if a Call Home thread should terminate.
2543 *
2544 * Checks the shared boolean variable thread_running. This should be done everytime
2545 * before entering a critical section.
2546 *
2547 * @param[in] data Call Home thread's data.
2548 *
2549 * @return 0 if the thread should stop running, -1 if it can continue.
2550 */
2551static int
2552nc_server_ch_client_thread_is_running(struct nc_ch_client_thread_arg *data)
2553{
2554 int ret = -1;
2555
2556 /* COND LOCK */
2557 pthread_mutex_lock(&data->cond_lock);
2558 if (!data->thread_running) {
2559 /* thread should stop running */
2560 ret = 0;
2561 }
2562 /* COND UNLOCK */
2563 pthread_mutex_unlock(&data->cond_lock);
2564
2565 return ret;
2566}
2567
Michal Vasko2e6defd2016-10-07 15:48:15 +02002568static void *
2569nc_ch_client_thread(void *arg)
2570{
2571 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2572 NC_MSG_TYPE msgtype;
2573 uint8_t cur_attempts = 0;
romanf578cd52023-10-19 09:47:40 +02002574 uint16_t next_endpt_index, max_wait;
Michal Vasko9550cf12017-03-21 15:33:58 +01002575 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002576 struct nc_ch_endpt *cur_endpt;
romanf578cd52023-10-19 09:47:40 +02002577 struct nc_session *session = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002578 struct nc_ch_client *client;
romanf578cd52023-10-19 09:47:40 +02002579 uint32_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002580
2581 /* LOCK */
2582 client = nc_server_ch_client_with_endpt_lock(data->client_name);
romanf578cd52023-10-19 09:47:40 +02002583 assert(client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002584
2585 cur_endpt = &client->ch_endpts[0];
2586 cur_endpt_name = strdup(cur_endpt->name);
2587
2588 while (1) {
romanf578cd52023-10-19 09:47:40 +02002589 if (!nc_server_ch_client_thread_is_running(data)) {
2590 /* thread should stop running */
2591 break;
2592 }
2593
Michal Vasko056f53c2022-10-21 13:38:15 +02002594 if (!cur_attempts) {
2595 VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" connecting...", data->client_name, cur_endpt_name);
2596 }
Michal Vasko58bac1c2022-03-24 15:25:26 +01002597 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 +02002598
2599 if (msgtype == NC_MSG_HELLO) {
2600 /* UNLOCK */
2601 nc_server_ch_client_unlock(client);
2602
romanf578cd52023-10-19 09:47:40 +02002603 if (!nc_server_ch_client_thread_is_running(data)) {
2604 /* thread should stop running */
2605 goto cleanup;
2606 }
2607
2608 /* run while the session is established */
2609 VRB(session, "Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002610 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2611 goto cleanup;
2612 }
romanf578cd52023-10-19 09:47:40 +02002613
2614 VRB(session, "Call Home client \"%s\" session terminated.", data->client_name);
2615 if (!nc_server_ch_client_thread_is_running(data)) {
2616 /* thread should stop running */
2617 goto cleanup;
2618 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002619
2620 /* LOCK */
2621 client = nc_server_ch_client_with_endpt_lock(data->client_name);
romanf578cd52023-10-19 09:47:40 +02002622 assert(client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002623
2624 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002625 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002626 if (client->conn_type == NC_CH_PERIOD) {
romanf578cd52023-10-19 09:47:40 +02002627 if (client->anchor_time) {
Michal Vasko18e1fa02021-11-29 09:02:05 +01002628 /* anchored */
romanf578cd52023-10-19 09:47:40 +02002629 reconnect_in = (time(NULL) - client->anchor_time) % (client->period * 60);
Michal Vasko18e1fa02021-11-29 09:02:05 +01002630 } else {
2631 /* fixed timeout */
romanf578cd52023-10-19 09:47:40 +02002632 reconnect_in = client->period * 60;
Michal Vasko18e1fa02021-11-29 09:02:05 +01002633 }
2634
Michal Vasko2e6defd2016-10-07 15:48:15 +02002635 /* UNLOCK */
2636 nc_server_ch_client_unlock(client);
2637
romanf578cd52023-10-19 09:47:40 +02002638 /* wait for the timeout to elapse, so we can try to reconnect */
2639 VRB(session, "Call Home client \"%s\" reconnecting in %" PRIu32 " seconds.", data->client_name, reconnect_in);
2640 if (!nc_server_ch_client_thread_is_running_wait(session, data, reconnect_in)) {
2641 goto cleanup;
2642 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002643
2644 /* LOCK */
2645 client = nc_server_ch_client_with_endpt_lock(data->client_name);
romanf578cd52023-10-19 09:47:40 +02002646 assert(client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002647 }
2648
2649 /* set next endpoint to try */
2650 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00002651 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002652 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00002653 /* we keep the current one but due to unlock/lock we have to find it again */
2654 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
2655 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
2656 break;
2657 }
2658 }
2659 if (next_endpt_index >= client->ch_endpt_count) {
2660 /* endpoint was removed, start with the first one */
2661 next_endpt_index = 0;
2662 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002663 } else {
2664 /* just get a random index */
2665 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00002666 }
2667
Michal Vasko2e6defd2016-10-07 15:48:15 +02002668 } else {
romanf578cd52023-10-19 09:47:40 +02002669 /* session was not created, wait a little bit and try again */
2670 max_wait = client->max_wait;
2671
Michal Vasko6bb116b2016-10-26 13:53:46 +02002672 /* UNLOCK */
2673 nc_server_ch_client_unlock(client);
2674
romanf578cd52023-10-19 09:47:40 +02002675 /* wait for max_wait seconds */
2676 if (!nc_server_ch_client_thread_is_running_wait(session, data, max_wait)) {
2677 /* thread should stop running */
2678 goto cleanup;
2679 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002680
Michal Vasko6bb116b2016-10-26 13:53:46 +02002681 /* LOCK */
2682 client = nc_server_ch_client_with_endpt_lock(data->client_name);
romanf578cd52023-10-19 09:47:40 +02002683 assert(client);
Michal Vasko6bb116b2016-10-26 13:53:46 +02002684
Michal Vasko2e6defd2016-10-07 15:48:15 +02002685 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02002686
2687 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00002688 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
2689 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02002690 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002691 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02002692 }
2693
Peter Feiged05f2252018-09-03 08:09:47 +00002694 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02002695 /* endpoint was removed, start with the first one */
romanf578cd52023-10-19 09:47:40 +02002696 VRB(session, "Call Home client \"%s\" endpoint \"%s\" removed.", data->client_name, cur_endpt_name);
Peter Feiged05f2252018-09-03 08:09:47 +00002697 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02002698 cur_attempts = 0;
2699 } else if (cur_attempts == client->max_attempts) {
2700 /* we have tried to connect to this endpoint enough times */
romanf578cd52023-10-19 09:47:40 +02002701 VRB(session, "Call Home client \"%s\" endpoint \"%s\" failed connection attempt limit %" PRIu8 " reached.",
Michal Vasko056f53c2022-10-21 13:38:15 +02002702 data->client_name, cur_endpt_name, client->max_attempts);
2703
2704 /* clear a pending socket, if any */
2705 cur_endpt = &client->ch_endpts[next_endpt_index];
2706 if (cur_endpt->sock_pending > -1) {
2707 close(cur_endpt->sock_pending);
2708 cur_endpt->sock_pending = -1;
2709 }
2710
Peter Feiged05f2252018-09-03 08:09:47 +00002711 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002712 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00002713 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002714 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02002715 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02002716 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002717 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002718 cur_attempts = 0;
2719 } /* else we keep the current one */
2720 }
Peter Feiged05f2252018-09-03 08:09:47 +00002721
2722 cur_endpt = &client->ch_endpts[next_endpt_index];
2723 free(cur_endpt_name);
2724 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002725 }
romanf578cd52023-10-19 09:47:40 +02002726 /* UNLOCK if we break out of the loop */
2727 nc_server_ch_client_unlock(client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002728
2729cleanup:
romanf578cd52023-10-19 09:47:40 +02002730 VRB(session, "Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01002731 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002732 free(data->client_name);
2733 free(data);
2734 return NULL;
2735}
2736
2737API int
Michal Vasko93224072021-11-09 12:14:28 +01002738nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
romanf578cd52023-10-19 09:47:40 +02002739 nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb,
2740 void *new_session_cb_data)
Michal Vasko3f05a092018-03-13 10:39:49 +01002741{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002742 int ret;
2743 pthread_t tid;
2744 struct nc_ch_client_thread_arg *arg;
romanf578cd52023-10-19 09:47:40 +02002745 uint16_t i;
2746 struct nc_ch_client *ch_client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002747
romanf578cd52023-10-19 09:47:40 +02002748 NC_CHECK_ARG_RET(NULL, client_name, acquire_ctx_cb, release_ctx_cb, new_session_cb, -1);
2749
2750 for (i = 0; i < server_opts.ch_client_count; i++) {
2751 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2752 ch_client = &server_opts.ch_clients[i];
2753 break;
2754 }
2755 }
2756
2757 if (i == server_opts.ch_client_count) {
2758 ERR(NULL, "Client \"%s\" not found.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002759 return -1;
2760 }
2761
2762 arg = malloc(sizeof *arg);
roman3a95bb22023-10-26 11:07:17 +02002763 NC_CHECK_ERRMEM_RET(!arg, -1);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002764 arg->client_name = strdup(client_name);
2765 if (!arg->client_name) {
2766 ERRMEM;
2767 free(arg);
2768 return -1;
2769 }
Michal Vasko93224072021-11-09 12:14:28 +01002770 arg->acquire_ctx_cb = acquire_ctx_cb;
2771 arg->release_ctx_cb = release_ctx_cb;
2772 arg->ctx_cb_data = ctx_cb_data;
2773 arg->new_session_cb = new_session_cb;
romanf578cd52023-10-19 09:47:40 +02002774 arg->new_session_cb_data = new_session_cb_data;
2775 /* thread is now running */
2776 arg->thread_running = 1;
2777 /* initialize the condition */
2778 pthread_cond_init(&arg->cond, NULL);
2779 /* initialize the mutex */
2780 pthread_mutex_init(&arg->cond_lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002781
2782 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2783 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002784 ERR(NULL, "Creating a new thread failed (%s).", strerror(ret));
Michal Vasko2e6defd2016-10-07 15:48:15 +02002785 free(arg->client_name);
2786 free(arg);
2787 return -1;
2788 }
2789 /* the thread now manages arg */
romanf578cd52023-10-19 09:47:40 +02002790 ch_client->tid = tid;
2791 ch_client->thread_data = arg;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002792
2793 return 0;
2794}
2795
romanf578cd52023-10-19 09:47:40 +02002796#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002797
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002798API time_t
2799nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002800{
romanf578cd52023-10-19 09:47:40 +02002801 NC_CHECK_ARG_RET(session, session, 0);
2802
2803 if (session->side != NC_SERVER) {
2804 ERRARG(session, "session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002805 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002806 }
2807
Michal Vasko2e6defd2016-10-07 15:48:15 +02002808 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002809}
Michal Vasko3486a7c2017-03-03 13:28:07 +01002810
2811API void
Michal Vasko71dbd772021-03-23 14:08:37 +01002812nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01002813{
2814 if (!session || (session->side != NC_SERVER)) {
romanf578cd52023-10-19 09:47:40 +02002815 ERRARG(session, "session");
Michal Vasko3486a7c2017-03-03 13:28:07 +01002816 return;
2817 }
2818
Michal Vaskodf68e7e2022-04-21 11:04:00 +02002819 /* NTF STATUS LOCK */
2820 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
2821
Michal Vasko71dbd772021-03-23 14:08:37 +01002822 ++session->opts.server.ntf_status;
Michal Vaskodf68e7e2022-04-21 11:04:00 +02002823
2824 /* NTF STATUS UNLOCK */
2825 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko71dbd772021-03-23 14:08:37 +01002826}
2827
2828API void
2829nc_session_dec_notif_status(struct nc_session *session)
2830{
2831 if (!session || (session->side != NC_SERVER)) {
romanf578cd52023-10-19 09:47:40 +02002832 ERRARG(session, "session");
Michal Vasko71dbd772021-03-23 14:08:37 +01002833 return;
2834 }
2835
Michal Vaskodf68e7e2022-04-21 11:04:00 +02002836 /* NTF STATUS LOCK */
2837 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
2838
Michal Vasko71dbd772021-03-23 14:08:37 +01002839 if (session->opts.server.ntf_status) {
2840 --session->opts.server.ntf_status;
2841 }
Michal Vaskodf68e7e2022-04-21 11:04:00 +02002842
2843 /* NTF STATUS UNLOCK */
2844 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko3486a7c2017-03-03 13:28:07 +01002845}
2846
2847API int
2848nc_session_get_notif_status(const struct nc_session *session)
2849{
Michal Vaskodf68e7e2022-04-21 11:04:00 +02002850 uint32_t ntf_status;
2851
Michal Vasko3486a7c2017-03-03 13:28:07 +01002852 if (!session || (session->side != NC_SERVER)) {
romanf578cd52023-10-19 09:47:40 +02002853 ERRARG(session, "session");
Michal Vasko3486a7c2017-03-03 13:28:07 +01002854 return 0;
2855 }
2856
Michal Vaskodf68e7e2022-04-21 11:04:00 +02002857 /* NTF STATUS LOCK */
2858 pthread_mutex_lock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
2859
2860 ntf_status = session->opts.server.ntf_status;
2861
2862 /* NTF STATUS UNLOCK */
2863 pthread_mutex_unlock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
2864
2865 return ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01002866}