blob: 9f01303625e0971eb62564450888dd27ca9ae640 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_server.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libnetconf2 server session manipulation functions
Michal Vasko086311b2016-01-08 09:53:11 +01005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
7 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01008 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01009 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010012 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010013 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010014 */
apropp-molex4e903c32020-04-20 03:06:58 -040015#define _QNX_SOURCE /* getpeereid */
Olivier Matzac7fa2f2018-10-11 10:02:04 +020016#define _GNU_SOURCE /* signals, threads, SO_PEERCRED */
Michal Vasko086311b2016-01-08 09:53:11 +010017
Michal Vaskob83a3fa2021-05-26 09:53:42 +020018#include <arpa/inet.h>
Michal Vasko77e83572022-07-21 15:31:15 +020019#include <assert.h>
Michal Vasko086311b2016-01-08 09:53:11 +010020#include <errno.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020021#include <fcntl.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020022#include <netinet/in.h>
23#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020024#include <poll.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010025#include <pthread.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020026#include <pwd.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020027#include <signal.h>
28#include <stdint.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/socket.h>
32#include <sys/types.h>
33#include <sys/un.h>
34#include <time.h>
35#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010036
Michal Vasko7a20d2e2021-05-19 16:40:23 +020037#include "compat.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010038#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010039#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020040#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010041
Michal Vaskob48aa812016-01-18 14:13:09 +010042struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010043#ifdef NC_ENABLED_SSH
44 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
45#endif
46 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020047 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
48 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010049};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010050
fanchanghu966f2de2016-07-21 02:28:57 -040051static nc_rpc_clb global_rpc_clb = NULL;
52
Michal Vasko3031aae2016-01-27 16:07:18 +010053struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010054nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010055{
56 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010057 struct nc_endpt *endpt = NULL;
58
Michal Vaskoddce1212019-05-24 09:58:49 +020059 if (!name) {
60 ERRARG("endpt_name");
61 return NULL;
62 }
63
Michal Vaskoade892d2017-02-22 13:40:35 +010064 /* WRITE LOCK */
65 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010066
67 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020068 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010069 endpt = &server_opts.endpts[i];
70 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010071 }
72 }
73
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010074 if (!endpt) {
Michal Vasko05532772021-06-03 12:12:38 +020075 ERR(NULL, "Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010076 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020077 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010078 return NULL;
79 }
80
Michal Vaskoe2713da2016-08-22 16:06:40 +020081 if (idx) {
82 *idx = i;
83 }
84
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010085 return endpt;
86}
87
Michal Vaskoadf30f02019-06-24 09:34:47 +020088struct nc_ch_endpt *
89nc_server_ch_client_lock(const char *name, const char *endpt_name, NC_TRANSPORT_IMPL ti, struct nc_ch_client **client_p)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010090{
Michal Vaskoadf30f02019-06-24 09:34:47 +020091 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020092 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020093 struct nc_ch_endpt *endpt = NULL;
94
95 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +020096
Michal Vaskoddce1212019-05-24 09:58:49 +020097 if (!name) {
98 ERRARG("client_name");
99 return NULL;
100 }
101
Michal Vasko2e6defd2016-10-07 15:48:15 +0200102 /* READ LOCK */
103 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
104
105 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200106 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200107 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200108 if (!endpt_name && !ti) {
109 /* return only client */
110 break;
111 }
112 for (j = 0; j < client->ch_endpt_count; ++j) {
Michal Vasko530d95c2021-05-28 13:32:02 +0200113 if ((!endpt_name || !strcmp(client->ch_endpts[j].name, endpt_name)) &&
114 (!ti || (ti == client->ch_endpts[j].ti))) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200115 endpt = &client->ch_endpts[j];
116 break;
117 }
118 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200119 break;
120 }
121 }
122
123 if (!client) {
Michal Vasko05532772021-06-03 12:12:38 +0200124 ERR(NULL, "Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200125
Michal Vasko2e6defd2016-10-07 15:48:15 +0200126 /* READ UNLOCK */
127 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200128 } else if (endpt_name && ti && !endpt) {
Michal Vasko05532772021-06-03 12:12:38 +0200129 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200130
131 /* READ UNLOCK */
132 pthread_rwlock_unlock(&server_opts.ch_client_lock);
133 } else {
134 /* CH CLIENT LOCK */
135 pthread_mutex_lock(&client->lock);
136
137 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200138 }
139
Michal Vaskoadf30f02019-06-24 09:34:47 +0200140 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200141}
142
143void
144nc_server_ch_client_unlock(struct nc_ch_client *client)
145{
146 /* CH CLIENT UNLOCK */
147 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100148
149 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200150 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100151}
Michal Vasko086311b2016-01-08 09:53:11 +0100152
Michal Vasko1a38c862016-01-15 15:50:07 +0100153API void
154nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
155{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200156 if (!session) {
157 ERRARG("session");
158 return;
159 } else if (!reason) {
160 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100161 return;
162 }
163
Michal Vasko142cfea2017-08-07 10:12:11 +0200164 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
165 session->killed_by = 0;
166 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100167 session->term_reason = reason;
168}
169
Michal Vasko142cfea2017-08-07 10:12:11 +0200170API void
171nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
172{
173 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
174 ERRARG("session");
175 return;
176 } else if (!sid) {
177 ERRARG("sid");
178 return;
179 }
180
181 session->killed_by = sid;
182}
183
184API void
185nc_session_set_status(struct nc_session *session, NC_STATUS status)
186{
187 if (!session) {
188 ERRARG("session");
189 return;
190 } else if (!status) {
191 ERRARG("status");
192 return;
193 }
194
195 session->status = status;
196}
197
Michal Vasko086311b2016-01-08 09:53:11 +0100198int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200199nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100200{
Michal Vasko06c860d2018-07-09 16:08:52 +0200201 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100202 int is_ipv4, sock;
203 struct sockaddr_storage saddr;
204
205 struct sockaddr_in *saddr4;
206 struct sockaddr_in6 *saddr6;
207
Michal Vasko086311b2016-01-08 09:53:11 +0100208 if (!strchr(address, ':')) {
209 is_ipv4 = 1;
210 } else {
211 is_ipv4 = 0;
212 }
213
214 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
215 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200216 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100217 goto fail;
218 }
219
Michal Vaskobe52dc22018-10-17 09:28:17 +0200220 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200221 opt = 1;
222 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200223 ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko06c860d2018-07-09 16:08:52 +0200224 goto fail;
225 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100226 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200227 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vasko83ad17e2019-01-30 10:11:37 +0100228 goto fail;
229 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200230
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200231 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100232 goto fail;
233 }
234
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200235 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100236 if (is_ipv4) {
237 saddr4 = (struct sockaddr_in *)&saddr;
238
239 saddr4->sin_family = AF_INET;
240 saddr4->sin_port = htons(port);
241
242 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200243 ERR(NULL, "Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100244 goto fail;
245 }
246
247 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200248 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100249 goto fail;
250 }
251
252 } else {
253 saddr6 = (struct sockaddr_in6 *)&saddr;
254
255 saddr6->sin6_family = AF_INET6;
256 saddr6->sin6_port = htons(port);
257
258 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200259 ERR(NULL, "Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100260 goto fail;
261 }
262
263 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200264 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100265 goto fail;
266 }
267 }
268
Michal Vaskofb89d772016-01-08 12:25:35 +0100269 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200270 ERR(NULL, "Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100271 goto fail;
272 }
273
274 return sock;
275
276fail:
277 if (sock > -1) {
278 close(sock);
279 }
280
281 return -1;
282}
283
284int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200285nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
286{
287 struct sockaddr_un sun;
288 int sock = -1;
289
Michal Vasko93e96f12021-09-30 10:02:09 +0200290 if (strlen(address) > sizeof(sun.sun_path) - 1) {
291 ERR(NULL, "Socket path \"%s\" is longer than maximum length %d.", address, (int)(sizeof(sun.sun_path) - 1));
292 goto fail;
293 }
294
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200295 sock = socket(AF_UNIX, SOCK_STREAM, 0);
296 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200297 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200298 goto fail;
299 }
300
301 memset(&sun, 0, sizeof(sun));
302 sun.sun_family = AF_UNIX;
Michal Vasko93e96f12021-09-30 10:02:09 +0200303 snprintf(sun.sun_path, sizeof(sun.sun_path) - 1, "%s", address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200304
305 unlink(sun.sun_path);
306 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200307 ERR(NULL, "Could not bind \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200308 goto fail;
309 }
310
311 if (opts->mode != (mode_t)-1) {
312 if (chmod(sun.sun_path, opts->mode) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200313 ERR(NULL, "Failed to set unix socket permissions (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200314 goto fail;
315 }
316 }
317
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200318 if ((opts->uid != (uid_t)-1) || (opts->gid != (gid_t)-1)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200319 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200320 ERR(NULL, "Failed to set unix socket uid/gid (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200321 goto fail;
322 }
323 }
324
325 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200326 ERR(NULL, "Unable to start listening on \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200327 goto fail;
328 }
329
330 return sock;
331
332fail:
333 if (sock > -1) {
334 close(sock);
335 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200336 return -1;
337}
338
aPiecek90ff0242021-02-14 14:58:01 +0100339/**
340 * @brief Evaluate socket name for AF_UNIX socket.
341 * @param[in] acc_sock_fd is file descriptor for the accepted socket (a nonnegative).
342 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
343 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
344 * @return 0 if the stream socket is unnamed. Parameter host is set to NULL.
345 * @return -1 in case of error. Parameter host is set to NULL.
346 */
347static int
348sock_host_unix(int acc_sock_fd, char **host)
349{
350 char *sun_path;
351 struct sockaddr_storage saddr;
352 socklen_t addr_len;
353
354 *host = NULL;
355 saddr.ss_family = AF_UNIX;
356 addr_len = sizeof(saddr);
357
358 if (getsockname(acc_sock_fd, (struct sockaddr *)&saddr, &addr_len)) {
Michal Vasko05532772021-06-03 12:12:38 +0200359 ERR(NULL, "getsockname failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100360 return -1;
361 }
362
363 sun_path = ((struct sockaddr_un *)&saddr)->sun_path;
364 if (!sun_path) {
365 /* stream socket is unnamed */
366 return 0;
367 }
368
369 if (!(*host = strdup(sun_path))) {
370 ERRMEM;
371 return -1;
372 }
373
374 return 0;
375}
376
377/**
378 * @brief Evaluate socket name and port number for AF_INET socket.
379 * @param[in] addr is pointing to structure filled by accept function which was successful.
380 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
381 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
382 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
383 * @return -1 in case of error. Parameter host is set to NULL and port is unchanged.
384 */
385static int
386sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port)
387{
388 *host = malloc(INET_ADDRSTRLEN);
389 if (!(*host)) {
390 ERRMEM;
391 return -1;
392 }
393
aPiecek3da9b342021-02-18 15:00:03 +0100394 if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko69e98752022-12-14 14:20:17 +0100395 ERR(NULL, "inet_ntop failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100396 free(*host);
397 *host = NULL;
398 return -1;
399 }
400
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200401 *port = ntohs(addr->sin_port);
aPiecek90ff0242021-02-14 14:58:01 +0100402
403 return 0;
404}
405
406/**
407 * @brief Evaluate socket name and port number for AF_INET6 socket.
408 * @param[in] addr is pointing to structure filled by accept function which was successful.
409 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
410 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
411 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
412 * @return -1 in case of error. Parameter host is set to the NULL and port is unchanged.
413 */
414static int
415sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port)
416{
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200417 *host = malloc(INET6_ADDRSTRLEN);
aPiecek90ff0242021-02-14 14:58:01 +0100418 if (!(*host)) {
419 ERRMEM;
420 return -1;
421 }
422
aPiecek3da9b342021-02-18 15:00:03 +0100423 if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko69e98752022-12-14 14:20:17 +0100424 ERR(NULL, "inet_ntop failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100425 free(*host);
426 *host = NULL;
427 return -1;
428 }
429
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200430 *port = ntohs(addr->sin6_port);
aPiecek90ff0242021-02-14 14:58:01 +0100431
432 return 0;
433}
434
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200435int
Michal Vasko3031aae2016-01-27 16:07:18 +0100436nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100437{
Michal Vaskof54cd352017-02-22 13:42:02 +0100438 sigset_t sigmask, origmask;
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200439 uint16_t i, j, pfd_count, client_port;
440 char *client_address;
Michal Vasko086311b2016-01-08 09:53:11 +0100441 struct pollfd *pfd;
442 struct sockaddr_storage saddr;
443 socklen_t saddr_len = sizeof(saddr);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200444 int ret, client_sock, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100445
446 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100447 if (!pfd) {
448 ERRMEM;
449 return -1;
450 }
451
Michal Vaskoac2f6182017-01-30 14:32:03 +0100452 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200453 if (binds[i].sock < 0) {
454 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200455 continue;
456 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200457 if (binds[i].pollin) {
458 binds[i].pollin = 0;
459 /* leftover pollin */
460 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100461 break;
462 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100463 pfd[pfd_count].fd = binds[i].sock;
464 pfd[pfd_count].events = POLLIN;
465 pfd[pfd_count].revents = 0;
466
467 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100468 }
469
Michal Vasko0a3f3752016-10-13 14:58:38 +0200470 if (sock == -1) {
471 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100472 sigfillset(&sigmask);
473 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100474 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100475 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
476
Michal Vasko0a3f3752016-10-13 14:58:38 +0200477 if (!ret) {
478 /* we timeouted */
479 free(pfd);
480 return 0;
481 } else if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200482 ERR(NULL, "Poll failed (%s).", strerror(errno));
Michal Vasko0a3f3752016-10-13 14:58:38 +0200483 free(pfd);
484 return -1;
485 }
Michal Vasko086311b2016-01-08 09:53:11 +0100486
Michal Vaskoac2f6182017-01-30 14:32:03 +0100487 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
488 /* adjust i so that indices in binds and pfd always match */
489 while (binds[i].sock != pfd[j].fd) {
490 ++i;
491 }
492
493 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200494 --ret;
495
496 if (!ret) {
497 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100498 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200499 break;
500 } else {
501 /* just remember the event for next time */
502 binds[i].pollin = 1;
503 }
504 }
Michal Vasko086311b2016-01-08 09:53:11 +0100505 }
506 }
507 free(pfd);
508
509 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100510 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100511 return -1;
512 }
513
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200514 /* accept connection */
515 client_sock = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
516 if (client_sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200517 ERR(NULL, "Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100518 return -1;
519 }
520
Michal Vasko0190bc32016-03-02 15:47:49 +0100521 /* make the socket non-blocking */
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200522 if (((flags = fcntl(client_sock, F_GETFL)) == -1) || (fcntl(client_sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200523 ERR(NULL, "Fcntl failed (%s).", strerror(errno));
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200524 goto fail;
Michal Vasko0190bc32016-03-02 15:47:49 +0100525 }
526
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200527 /* learn information about the client end */
528 if (saddr.ss_family == AF_UNIX) {
529 if (sock_host_unix(client_sock, &client_address)) {
530 goto fail;
531 }
532 client_port = 0;
533 } else if (saddr.ss_family == AF_INET) {
534 if (sock_host_inet((struct sockaddr_in *)&saddr, &client_address, &client_port)) {
535 goto fail;
536 }
537 } else if (saddr.ss_family == AF_INET6) {
538 if (sock_host_inet6((struct sockaddr_in6 *)&saddr, &client_address, &client_port)) {
539 goto fail;
540 }
541 } else {
542 ERR(NULL, "Source host of an unknown protocol family.");
543 goto fail;
aPiecek90ff0242021-02-14 14:58:01 +0100544 }
Michal Vasko086311b2016-01-08 09:53:11 +0100545
aPiecek90ff0242021-02-14 14:58:01 +0100546 if (saddr.ss_family == AF_UNIX) {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200547 VRB(NULL, "Accepted a connection on %s.", binds[i].address);
aPiecek90ff0242021-02-14 14:58:01 +0100548 } else {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200549 VRB(NULL, "Accepted a connection on %s:%u from %s:%u.", binds[i].address, binds[i].port, client_address, client_port);
Michal Vasko086311b2016-01-08 09:53:11 +0100550 }
551
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200552 if (host) {
553 *host = client_address;
554 } else {
555 free(client_address);
556 }
557 if (port) {
558 *port = client_port;
559 }
560 if (idx) {
561 *idx = i;
562 }
563 return client_sock;
564
565fail:
566 close(client_sock);
567 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100568}
569
Michal Vasko238b6c12021-12-14 15:14:09 +0100570API struct nc_server_reply *
Michal Vasko05532772021-06-03 12:12:38 +0200571nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100572{
Michal Vasko77367452021-02-16 16:32:18 +0100573 const char *identifier = NULL, *revision = NULL, *format = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100574 char *model_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100575 struct ly_out *out;
Michal Vasko9b1a9522021-03-15 16:24:26 +0100576 const struct lys_module *module = NULL, *mod;
Michal Vasko77367452021-02-16 16:32:18 +0100577 const struct lysp_submodule *submodule = NULL;
578 struct lyd_node *child, *err, *data = NULL;
579 LYS_OUTFORMAT outformat = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100580
Michal Vasko77367452021-02-16 16:32:18 +0100581 LY_LIST_FOR(lyd_child(rpc), child) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100582 if (!strcmp(child->schema->name, "identifier")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200583 identifier = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100584 } else if (!strcmp(child->schema->name, "version")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200585 revision = lyd_get_value(child);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200586 if (revision && (revision[0] == '\0')) {
Michal Vasko77367452021-02-16 16:32:18 +0100587 revision = NULL;
Radek Krejci1afa7792017-03-26 11:24:16 -0500588 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100589 } else if (!strcmp(child->schema->name, "format")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200590 format = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100591 }
592 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200593 VRB(session, "Module \"%s@%s\" was requested.", identifier, revision ? revision : "<any>");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100594
Michal Vasko77367452021-02-16 16:32:18 +0100595 /* check revision */
596 if (revision && (strlen(revision) != 10) && strcmp(revision, "1.0")) {
Michal Vasko93224072021-11-09 12:14:28 +0100597 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100598 nc_err_set_msg(err, "The requested version is not supported.", "en");
599 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100600 }
601
Michal Vasko77367452021-02-16 16:32:18 +0100602 if (revision) {
603 /* get specific module */
Michal Vasko93224072021-11-09 12:14:28 +0100604 module = ly_ctx_get_module(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100605 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100606 submodule = ly_ctx_get_submodule(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100607 }
608 } else {
609 /* try to get implemented, then latest module */
Michal Vasko93224072021-11-09 12:14:28 +0100610 module = ly_ctx_get_module_implemented(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100611 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100612 module = ly_ctx_get_module_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100613 }
614 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100615 submodule = ly_ctx_get_submodule_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100616 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200617 }
Michal Vasko77367452021-02-16 16:32:18 +0100618 if (!module && !submodule) {
Michal Vasko93224072021-11-09 12:14:28 +0100619 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko5ca5d972022-09-14 13:51:31 +0200620 nc_err_set_msg(err, "The requested module was not found.", "en");
Michal Vasko1a38c862016-01-15 15:50:07 +0100621 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100622 }
623
624 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100625 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko77367452021-02-16 16:32:18 +0100626 outformat = LYS_OUT_YANG;
Radek Krejci90fba642016-12-07 15:59:45 +0100627 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko77367452021-02-16 16:32:18 +0100628 outformat = LYS_OUT_YIN;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100629 } else {
Michal Vasko93224072021-11-09 12:14:28 +0100630 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100631 nc_err_set_msg(err, "The requested format is not supported.", "en");
632 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100633 }
Michal Vasko77367452021-02-16 16:32:18 +0100634
635 /* print */
636 ly_out_new_memory(&model_data, 0, &out);
637 if (module) {
638 lys_print_module(out, module, outformat, 0, 0);
639 } else {
640 lys_print_submodule(out, submodule, outformat, 0, 0);
641 }
642 ly_out_free(out, NULL, 0);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200643 if (!model_data) {
644 ERRINT;
645 return NULL;
646 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100647
Michal Vasko9b1a9522021-03-15 16:24:26 +0100648 /* create reply */
Michal Vasko93224072021-11-09 12:14:28 +0100649 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vasko9b1a9522021-03-15 16:24:26 +0100650 if (!mod || lyd_new_inner(NULL, mod, "get-schema", 0, &data)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100651 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200652 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100653 return NULL;
654 }
Michal Vasko9b1a9522021-03-15 16:24:26 +0100655 if (lyd_new_any(data, NULL, "data", model_data, 1, LYD_ANYDATA_STRING, 1, NULL)) {
656 ERRINT;
Michal Vaskoa50f68e2022-02-24 16:10:54 +0100657 free(model_data);
Michal Vasko9b1a9522021-03-15 16:24:26 +0100658 lyd_free_tree(data);
659 return NULL;
660 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100661
Radek Krejci36dfdb32016-09-01 16:56:35 +0200662 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100663}
664
Michal Vasko238b6c12021-12-14 15:14:09 +0100665API struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100666nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100667{
Michal Vasko428087d2016-01-14 16:04:28 +0100668 session->term_reason = NC_SESSION_TERM_CLOSED;
669 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100670}
671
Michal Vasko93224072021-11-09 12:14:28 +0100672/**
673 * @brief Initialize a context with default RPC callbacks if none are set.
674 *
675 * @param[in] ctx Context to initialize.
676 */
677static void
678nc_server_init_ctx(const struct ly_ctx *ctx)
Michal Vasko086311b2016-01-08 09:53:11 +0100679{
Michal Vasko77367452021-02-16 16:32:18 +0100680 struct lysc_node *rpc;
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100681
Michal Vasko238b6c12021-12-14 15:14:09 +0100682 if (global_rpc_clb) {
683 /* expect it to handle these RPCs as well */
684 return;
685 }
686
Michal Vasko05ba9df2016-01-13 14:40:27 +0100687 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100688 rpc = NULL;
689 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
690 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
691 }
Michal Vasko88639e92017-08-03 14:38:10 +0200692 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100693 rpc->priv = nc_clb_default_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100694 }
695
Michal Vasko93224072021-11-09 12:14:28 +0100696 /* set default <close-session> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100697 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200698 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100699 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100700 }
Michal Vasko93224072021-11-09 12:14:28 +0100701}
Michal Vasko05ba9df2016-01-13 14:40:27 +0100702
Michal Vasko93224072021-11-09 12:14:28 +0100703API int
704nc_server_init(void)
705{
706 pthread_rwlockattr_t attr, *attr_p = NULL;
707 int r;
708
709 nc_init();
Michal Vaskob48aa812016-01-18 14:13:09 +0100710
711 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500712 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100713
Michal Vasko93224072021-11-09 12:14:28 +0100714#ifdef HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
715 if ((r = pthread_rwlockattr_init(&attr))) {
716 ERR(NULL, "%s: failed init attribute (%s).", __func__, strerror(r));
717 goto error;
718 }
719 attr_p = &attr;
720 if ((r = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))) {
721 ERR(NULL, "%s: failed set attribute (%s).", __func__, strerror(r));
722 goto error;
723 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700724#endif
Michal Vasko93224072021-11-09 12:14:28 +0100725
726 if ((r = pthread_rwlock_init(&server_opts.endpt_lock, attr_p))) {
727 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
728 goto error;
729 }
730 if ((r = pthread_rwlock_init(&server_opts.ch_client_lock, attr_p))) {
731 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
732 goto error;
733 }
734
735 if (attr_p) {
736 pthread_rwlockattr_destroy(attr_p);
Frank Rimpler9f838b02018-07-25 06:44:03 +0000737 }
Michal Vasko086311b2016-01-08 09:53:11 +0100738 return 0;
Michal Vasko93224072021-11-09 12:14:28 +0100739
740error:
741 if (attr_p) {
742 pthread_rwlockattr_destroy(attr_p);
743 }
744 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100745}
746
Michal Vaskob48aa812016-01-18 14:13:09 +0100747API void
748nc_server_destroy(void)
749{
Michal Vasko1440a742021-03-31 11:11:03 +0200750 uint32_t i;
Radek Krejci658782b2016-12-04 22:04:55 +0100751
752 for (i = 0; i < server_opts.capabilities_count; i++) {
Michal Vasko93224072021-11-09 12:14:28 +0100753 free(server_opts.capabilities[i]);
Radek Krejci658782b2016-12-04 22:04:55 +0100754 }
755 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200756 server_opts.capabilities = NULL;
757 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200758 if (server_opts.content_id_data && server_opts.content_id_data_free) {
759 server_opts.content_id_data_free(server_opts.content_id_data);
760 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200761
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200762#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100763 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200764 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100765#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100766#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100767 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
768 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
769 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200770 server_opts.passwd_auth_data = NULL;
771 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100772
Michal Vasko1c2d2652023-10-17 08:53:36 +0200773 if (server_opts.pubkey_auth_data && server_opts.pubkey_auth_data_free) {
774 server_opts.pubkey_auth_data_free(server_opts.pubkey_auth_data);
775 }
776 server_opts.pubkey_auth_data = NULL;
777 server_opts.pubkey_auth_data_free = NULL;
778
779 if (server_opts.interactive_auth_sess_data && server_opts.interactive_auth_sess_data_free) {
780 server_opts.interactive_auth_sess_data_free(server_opts.interactive_auth_sess_data);
781 }
782 server_opts.interactive_auth_sess_data = NULL;
783 server_opts.interactive_auth_sess_data_free = NULL;
784
785 if (server_opts.interactive_auth_data && server_opts.interactive_auth_data_free) {
786 server_opts.interactive_auth_data_free(server_opts.interactive_auth_data);
787 }
788 server_opts.interactive_auth_data = NULL;
789 server_opts.interactive_auth_data_free = NULL;
790
Michal Vasko17dfda92016-12-01 14:06:16 +0100791 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100792
793 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
794 server_opts.hostkey_data_free(server_opts.hostkey_data);
795 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200796 server_opts.hostkey_data = NULL;
797 server_opts.hostkey_data_free = NULL;
roman41a11e42022-06-22 09:27:08 +0200798
799 /* PAM */
800 free(server_opts.conf_name);
801 free(server_opts.conf_dir);
802 server_opts.conf_name = NULL;
803 server_opts.conf_dir = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100804#endif
805#ifdef NC_ENABLED_TLS
806 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
807 server_opts.server_cert_data_free(server_opts.server_cert_data);
808 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200809 server_opts.server_cert_data = NULL;
810 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100811 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
812 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
813 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200814 server_opts.trusted_cert_list_data = NULL;
815 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100816#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100817 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100818}
819
Michal Vasko086311b2016-01-08 09:53:11 +0100820API int
821nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
822{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200823 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
824 ERRARG("basic_mode");
825 return -1;
826 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
827 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100828 return -1;
829 }
830
831 server_opts.wd_basic_mode = basic_mode;
832 server_opts.wd_also_supported = also_supported;
833 return 0;
834}
835
Michal Vasko1a38c862016-01-15 15:50:07 +0100836API void
Michal Vasko55f03972016-04-13 08:56:01 +0200837nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
838{
839 if (!basic_mode && !also_supported) {
840 ERRARG("basic_mode and also_supported");
841 return;
842 }
843
844 if (basic_mode) {
845 *basic_mode = server_opts.wd_basic_mode;
846 }
847 if (also_supported) {
848 *also_supported = server_opts.wd_also_supported;
849 }
850}
851
Michal Vasko55f03972016-04-13 08:56:01 +0200852API int
Radek Krejci658782b2016-12-04 22:04:55 +0100853nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200854{
Michal Vasko93224072021-11-09 12:14:28 +0100855 void *mem;
Radek Krejci658782b2016-12-04 22:04:55 +0100856
857 if (!value || !value[0]) {
858 ERRARG("value must not be empty");
859 return EXIT_FAILURE;
860 }
861
Michal Vasko93224072021-11-09 12:14:28 +0100862 mem = realloc(server_opts.capabilities, (server_opts.capabilities_count + 1) * sizeof *server_opts.capabilities);
863 if (!mem) {
Radek Krejci658782b2016-12-04 22:04:55 +0100864 ERRMEM;
865 return EXIT_FAILURE;
866 }
Michal Vasko93224072021-11-09 12:14:28 +0100867 server_opts.capabilities = mem;
868
869 server_opts.capabilities[server_opts.capabilities_count] = strdup(value);
870 server_opts.capabilities_count++;
Radek Krejci658782b2016-12-04 22:04:55 +0100871
872 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200873}
874
Michal Vasko1a38c862016-01-15 15:50:07 +0100875API void
Michal Vasko1440a742021-03-31 11:11:03 +0200876nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
877 void (*free_user_data)(void *user_data))
878{
879 server_opts.content_id_clb = content_id_clb;
880 server_opts.content_id_data = user_data;
881 server_opts.content_id_data_free = free_user_data;
882}
883
884API void
Michal Vasko086311b2016-01-08 09:53:11 +0100885nc_server_set_hello_timeout(uint16_t hello_timeout)
886{
Michal Vasko086311b2016-01-08 09:53:11 +0100887 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100888}
889
Michal Vasko55f03972016-04-13 08:56:01 +0200890API uint16_t
891nc_server_get_hello_timeout(void)
892{
893 return server_opts.hello_timeout;
894}
895
Michal Vasko1a38c862016-01-15 15:50:07 +0100896API void
Michal Vasko086311b2016-01-08 09:53:11 +0100897nc_server_set_idle_timeout(uint16_t idle_timeout)
898{
Michal Vasko086311b2016-01-08 09:53:11 +0100899 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100900}
901
Michal Vasko55f03972016-04-13 08:56:01 +0200902API uint16_t
903nc_server_get_idle_timeout(void)
904{
905 return server_opts.idle_timeout;
906}
907
Michal Vasko71090fc2016-05-24 16:37:28 +0200908API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +0100909nc_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 +0100910{
Michal Vasko71090fc2016-05-24 16:37:28 +0200911 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200912 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200913
Michal Vasko93224072021-11-09 12:14:28 +0100914 if (!ctx) {
915 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +0200916 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200917 } else if (fdin < 0) {
918 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200919 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200920 } else if (fdout < 0) {
921 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200922 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200923 } else if (!username) {
924 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200925 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200926 } else if (!session) {
927 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200928 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100929 }
930
Michal Vasko93224072021-11-09 12:14:28 +0100931 /* init ctx as needed */
932 nc_server_init_ctx(ctx);
933
Michal Vasko086311b2016-01-08 09:53:11 +0100934 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200935 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100936 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100937 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200938 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100939 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100940 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100941
Michal Vasko086311b2016-01-08 09:53:11 +0100942 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100943 (*session)->ti_type = NC_TI_FD;
944 (*session)->ti.fd.in = fdin;
945 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100946
Michal Vasko93224072021-11-09 12:14:28 +0100947 /* assign context */
Michal Vasko1a38c862016-01-15 15:50:07 +0100948 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +0100949 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100950
Michal Vaskob48aa812016-01-18 14:13:09 +0100951 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200952 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100953
Michal Vasko086311b2016-01-08 09:53:11 +0100954 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200955 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200956 if (msgtype != NC_MSG_HELLO) {
957 nc_session_free(*session, NULL);
958 *session = NULL;
959 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100960 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200961
Michal Vaskod8a74192023-02-06 15:51:50 +0100962 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +0200963 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +0100964 nc_realtime_get(&ts_cur);
Michal Vasko9fb42272017-10-05 13:50:05 +0200965 (*session)->opts.server.session_start = ts_cur.tv_sec;
966
Michal Vasko1a38c862016-01-15 15:50:07 +0100967 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100968
Michal Vasko71090fc2016-05-24 16:37:28 +0200969 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100970}
Michal Vasko9e036d52016-01-08 10:49:26 +0100971
Michal Vaskob30b99c2016-07-26 11:35:43 +0200972static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100973nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
974{
975 uint8_t q_last;
976
977 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
978 ERRINT;
979 return;
980 }
981
982 /* get a unique queue value (by adding 1 to the last added value, if any) */
983 if (ps->queue_len) {
984 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
985 *id = ps->queue[q_last] + 1;
986 } else {
987 *id = 0;
988 }
989
990 /* add the id into the queue */
991 ++ps->queue_len;
992 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
993 ps->queue[q_last] = *id;
994}
995
996static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200997nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
998{
Michal Vasko74c345f2018-02-07 10:37:11 +0100999 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001000
1001 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +01001002 /* get the actual queue idx */
1003 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001004
1005 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +01001006 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +02001007 /* another equal value, simply cannot be */
1008 ERRINT;
1009 }
Michal Vaskod8340032018-02-12 14:41:00 +01001010 if (found == 2) {
1011 /* move the following values */
1012 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
1013 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001014 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +02001015 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +01001016 if (i == 0) {
1017 found = 1;
1018 } else {
1019 /* this is not okay, our id is in the middle of the queue */
1020 found = 2;
1021 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001022 }
1023 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001024 if (!found) {
1025 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +01001026 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001027 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001028
Michal Vasko103fe632018-02-12 16:37:45 +01001029 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +01001030 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +01001031 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +01001032 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
1033 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001034}
1035
Michal Vaskof04a52a2016-04-07 10:52:10 +02001036int
Michal Vasko26043172016-07-26 14:08:59 +02001037nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001038{
1039 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001040 struct timespec ts;
1041
Michal Vaskobe86fe32016-04-07 10:43:03 +02001042 /* LOCK */
Michal Vasko8c7def52023-06-06 14:48:56 +02001043 ret = pthread_mutex_lock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001044 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001045 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001046 return -1;
1047 }
1048
Michal Vasko74c345f2018-02-07 10:37:11 +01001049 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +01001050 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko05532772021-06-03 12:12:38 +02001051 ERR(NULL, "%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +01001052 pthread_mutex_unlock(&ps->lock);
1053 return -1;
1054 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001055
1056 /* add ourselves into the queue */
1057 nc_ps_queue_add_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001058 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 +02001059 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001060
1061 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001062 while (ps->queue[ps->queue_begin] != *id) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001063 nc_timeouttime_get(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001064
Michal Vaskod8a74192023-02-06 15:51:50 +01001065 ret = pthread_cond_clockwait(&ps->cond, &ps->lock, COMPAT_CLOCK_ID, &ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001066 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301067 /**
1068 * This may happen when another thread releases the lock and broadcasts the condition
1069 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1070 * but when actually this thread was ready for condition.
1071 */
preetbhansali629dfc42018-12-17 16:04:40 +05301072 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301073 break;
1074 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001075
Michal Vasko05532772021-06-03 12:12:38 +02001076 ERR(NULL, "%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001077 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001078 nc_ps_queue_remove_id(ps, *id);
1079 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001080 return -1;
1081 }
1082 }
1083
Michal Vaskobe86fe32016-04-07 10:43:03 +02001084 /* UNLOCK */
1085 pthread_mutex_unlock(&ps->lock);
1086
1087 return 0;
1088}
1089
Michal Vaskof04a52a2016-04-07 10:52:10 +02001090int
Michal Vasko26043172016-07-26 14:08:59 +02001091nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001092{
1093 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001094
1095 /* LOCK */
Michal Vasko8c7def52023-06-06 14:48:56 +02001096 ret = pthread_mutex_lock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001097 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001098 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001099 ret = -1;
1100 }
1101
Michal Vaskob30b99c2016-07-26 11:35:43 +02001102 /* we must be the first, it was our turn after all, right? */
1103 if (ps->queue[ps->queue_begin] != id) {
1104 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001105 /* UNLOCK */
1106 if (!ret) {
1107 pthread_mutex_unlock(&ps->lock);
1108 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001109 return -1;
1110 }
1111
Michal Vaskobe86fe32016-04-07 10:43:03 +02001112 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001113 nc_ps_queue_remove_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001114 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 +02001115 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001116
1117 /* broadcast to all other threads that the queue moved */
1118 pthread_cond_broadcast(&ps->cond);
1119
Michal Vaskobe86fe32016-04-07 10:43:03 +02001120 /* UNLOCK */
1121 if (!ret) {
1122 pthread_mutex_unlock(&ps->lock);
1123 }
1124
1125 return ret;
1126}
1127
Michal Vasko428087d2016-01-14 16:04:28 +01001128API struct nc_pollsession *
1129nc_ps_new(void)
1130{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001131 struct nc_pollsession *ps;
1132
1133 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001134 if (!ps) {
1135 ERRMEM;
1136 return NULL;
1137 }
Michal Vaskobe86fe32016-04-07 10:43:03 +02001138 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001139 pthread_mutex_init(&ps->lock, NULL);
1140
1141 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001142}
1143
1144API void
1145nc_ps_free(struct nc_pollsession *ps)
1146{
fanchanghu3d4e7212017-08-09 09:42:30 +08001147 uint16_t i;
1148
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001149 if (!ps) {
1150 return;
1151 }
1152
Michal Vaskobe86fe32016-04-07 10:43:03 +02001153 if (ps->queue_len) {
Michal Vasko05532772021-06-03 12:12:38 +02001154 ERR(NULL, "FATAL: Freeing a pollsession structure that is currently being worked with!");
Michal Vaskobe86fe32016-04-07 10:43:03 +02001155 }
1156
fanchanghu3d4e7212017-08-09 09:42:30 +08001157 for (i = 0; i < ps->session_count; i++) {
1158 free(ps->sessions[i]);
1159 }
1160
Michal Vasko428087d2016-01-14 16:04:28 +01001161 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001162 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001163 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001164
Michal Vasko428087d2016-01-14 16:04:28 +01001165 free(ps);
1166}
1167
1168API int
1169nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1170{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001171 uint8_t q_id;
1172
Michal Vasko45e53ae2016-04-07 11:46:03 +02001173 if (!ps) {
1174 ERRARG("ps");
1175 return -1;
1176 } else if (!session) {
1177 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001178 return -1;
1179 }
1180
Michal Vasko48a63ed2016-03-01 09:48:21 +01001181 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001182 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001183 return -1;
1184 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001185
Michal Vasko428087d2016-01-14 16:04:28 +01001186 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001187 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001188 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001189 ERRMEM;
1190 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001191 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001192 return -1;
1193 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001194 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1195 if (!ps->sessions[ps->session_count - 1]) {
1196 ERRMEM;
1197 --ps->session_count;
1198 /* UNLOCK */
1199 nc_ps_unlock(ps, q_id, __func__);
1200 return -1;
1201 }
1202 ps->sessions[ps->session_count - 1]->session = session;
1203 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001204
Michal Vasko48a63ed2016-03-01 09:48:21 +01001205 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001206 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001207}
1208
Michal Vasko48a63ed2016-03-01 09:48:21 +01001209static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001210_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001211{
1212 uint16_t i;
1213
Radek Krejcid5f978f2016-03-03 13:14:45 +01001214 if (index >= 0) {
1215 i = (uint16_t)index;
1216 goto remove;
1217 }
Michal Vasko428087d2016-01-14 16:04:28 +01001218 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001219 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001220remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001221 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001222 if (i <= ps->session_count) {
1223 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001224 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001225 }
1226 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001227 free(ps->sessions);
1228 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001229 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001230 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001231 return 0;
1232 }
1233 }
1234
Michal Vaskof0537d82016-01-29 14:42:38 +01001235 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001236}
1237
Michal Vasko48a63ed2016-03-01 09:48:21 +01001238API int
1239nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1240{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001241 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001242 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001243
Michal Vasko45e53ae2016-04-07 11:46:03 +02001244 if (!ps) {
1245 ERRARG("ps");
1246 return -1;
1247 } else if (!session) {
1248 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001249 return -1;
1250 }
1251
1252 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001253 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001254 return -1;
1255 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001256
Radek Krejcid5f978f2016-03-03 13:14:45 +01001257 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001258
1259 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001260 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001261
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001262 return ret || ret2 ? -1 : 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001263}
1264
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001265API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001266nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001267{
1268 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001269 struct nc_session *ret = NULL;
1270
1271 if (!ps) {
1272 ERRARG("ps");
1273 return NULL;
1274 }
1275
1276 /* LOCK */
1277 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1278 return NULL;
1279 }
1280
Michal Vasko4871c9d2017-10-09 14:48:39 +02001281 if (idx < ps->session_count) {
1282 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001283 }
1284
1285 /* UNLOCK */
1286 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1287
1288 return ret;
1289}
1290
Michal Vasko3ec3b112022-07-21 12:32:33 +02001291API struct nc_session *
1292nc_ps_find_session(const struct nc_pollsession *ps, nc_ps_session_match_cb match_cb, void *cb_data)
1293{
1294 uint8_t q_id;
1295 uint16_t i;
1296 struct nc_session *ret = NULL;
1297
1298 if (!ps) {
1299 ERRARG("ps");
1300 return NULL;
1301 }
1302
1303 /* LOCK */
1304 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1305 return NULL;
1306 }
1307
1308 for (i = 0; i < ps->session_count; ++i) {
1309 if (match_cb(ps->sessions[i]->session, cb_data)) {
1310 ret = ps->sessions[i]->session;
1311 break;
1312 }
1313 }
1314
1315 /* UNLOCK */
1316 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1317
1318 return ret;
1319}
1320
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001321API uint16_t
1322nc_ps_session_count(struct nc_pollsession *ps)
1323{
Michal Vasko47003942019-03-14 12:25:23 +01001324 uint8_t q_id;
1325 uint16_t session_count;
1326
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001327 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001328 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001329 return 0;
1330 }
1331
Michal Vasko47003942019-03-14 12:25:23 +01001332 /* LOCK (just for memory barrier so that we read the current value) */
1333 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1334 return 0;
1335 }
1336
1337 session_count = ps->session_count;
1338
1339 /* UNLOCK */
1340 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1341
1342 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001343}
1344
Michal Vasko77e83572022-07-21 15:31:15 +02001345static NC_MSG_TYPE
1346recv_rpc_check_msgid(struct nc_session *session, const struct lyd_node *envp)
1347{
1348 struct lyd_attr *attr;
1349
1350 assert(envp && !envp->schema);
1351
1352 /* find the message-id attribute */
1353 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1354 if (!strcmp(attr->name.name, "message-id")) {
1355 break;
1356 }
1357 }
1358
1359 if (!attr) {
1360 ERR(session, "Received an <rpc> without a message-id.");
1361 return NC_MSG_REPLY_ERR_MSGID;
1362 }
1363
1364 return NC_MSG_RPC;
1365}
1366
Michal Vasko131120a2018-05-29 15:44:02 +02001367/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001368 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001369 * NC_PSPOLL_TIMEOUT,
Michal Vaskof8fba542023-10-23 12:03:50 +02001370 * NC_PSPOLL_BAD_RPC (| NC_PSPOLL_REPLY_ERROR),
Michal Vasko71090fc2016-05-24 16:37:28 +02001371 * NC_PSPOLL_RPC
1372 */
1373static int
Michal Vasko131120a2018-05-29 15:44:02 +02001374nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001375{
Michal Vasko77367452021-02-16 16:32:18 +01001376 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001377 struct nc_server_reply *reply = NULL;
Michal Vasko939ffce2021-04-12 13:02:01 +02001378 struct lyd_node *e;
Michal Vaskof8fba542023-10-23 12:03:50 +02001379 int r, ret = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001380
Michal Vasko45e53ae2016-04-07 11:46:03 +02001381 if (!session) {
1382 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001383 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001384 } else if (!rpc) {
1385 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001386 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001387 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vasko05532772021-06-03 12:12:38 +02001388 ERR(session, "Invalid session to receive RPCs.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001389 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001390 }
1391
Michal Vasko93224072021-11-09 12:14:28 +01001392 *rpc = NULL;
1393
Michal Vasko77367452021-02-16 16:32:18 +01001394 /* get a message */
1395 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1396 if (r == -2) {
1397 /* malformed message */
Michal Vasko93224072021-11-09 12:14:28 +01001398 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77e83572022-07-21 15:31:15 +02001399 goto cleanup;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001400 }
1401 if (r == -1) {
Michal Vasko77367452021-02-16 16:32:18 +01001402 return NC_PSPOLL_ERROR;
1403 } else if (!r) {
1404 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001405 }
1406
Michal Vasko77367452021-02-16 16:32:18 +01001407 *rpc = calloc(1, sizeof **rpc);
1408 if (!*rpc) {
1409 ERRMEM;
Michal Vaskof8fba542023-10-23 12:03:50 +02001410 ret = NC_PSPOLL_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001411 goto cleanup;
1412 }
1413
1414 /* parse the RPC */
Michal Vasko77e83572022-07-21 15:31:15 +02001415 if (!lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_RPC_NETCONF, &(*rpc)->envp, &(*rpc)->rpc)) {
1416 /* check message-id */
1417 if (recv_rpc_check_msgid(session, (*rpc)->envp) == NC_MSG_RPC) {
1418 /* valid RPC */
1419 ret = NC_PSPOLL_RPC;
1420 } else {
1421 /* no message-id */
Michal Vasko77e83572022-07-21 15:31:15 +02001422 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MISSING_ATTR, NC_ERR_TYPE_RPC, "message-id", "rpc"));
1423 }
1424 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001425 /* bad RPC received */
Michal Vasko77367452021-02-16 16:32:18 +01001426 if ((*rpc)->envp) {
1427 /* at least the envelopes were parsed */
Michal Vasko93224072021-11-09 12:14:28 +01001428 e = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
1429 nc_err_set_msg(e, ly_errmsg(session->ctx), "en");
Michal Vasko939ffce2021-04-12 13:02:01 +02001430 reply = nc_server_reply_err(e);
Michal Vasko77367452021-02-16 16:32:18 +01001431 } else if (session->version == NC_VERSION_11) {
Michal Vasko93224072021-11-09 12:14:28 +01001432 /* completely malformed message, NETCONF version 1.1 defines sending error reply from
1433 * the server (RFC 6241 sec. 3) */
1434 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vaskof8fba542023-10-23 12:03:50 +02001435 } else {
1436 /* at least set the return value */
1437 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko77367452021-02-16 16:32:18 +01001438 }
Michal Vasko77367452021-02-16 16:32:18 +01001439 }
1440
1441cleanup:
Michal Vasko77e83572022-07-21 15:31:15 +02001442 if (reply) {
1443 /* send error reply */
1444 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, *rpc ? (*rpc)->envp : NULL, reply);
1445 nc_server_reply_free(reply);
1446 if (r != NC_MSG_REPLY) {
1447 ERR(session, "Failed to write reply (%s), terminating session.", nc_msgtype2str[r]);
1448 if (session->status != NC_STATUS_INVALID) {
1449 session->status = NC_STATUS_INVALID;
1450 session->term_reason = NC_SESSION_TERM_OTHER;
1451 }
1452 }
Michal Vaskof8fba542023-10-23 12:03:50 +02001453
1454 /* bad RPC and an error reply sent */
1455 ret = NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR;
Michal Vasko77e83572022-07-21 15:31:15 +02001456 }
1457
Michal Vasko77367452021-02-16 16:32:18 +01001458 ly_in_free(msg, 1);
1459 if (ret != NC_PSPOLL_RPC) {
1460 nc_server_rpc_free(*rpc);
1461 *rpc = NULL;
1462 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001463 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001464}
1465
fanchanghu966f2de2016-07-21 02:28:57 -04001466API void
1467nc_set_global_rpc_clb(nc_rpc_clb clb)
1468{
1469 global_rpc_clb = clb;
1470}
1471
Radek Krejci93e80222016-10-03 13:34:25 +02001472API NC_MSG_TYPE
1473nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1474{
Michal Vasko131120a2018-05-29 15:44:02 +02001475 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001476
1477 /* check parameters */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001478 if (!session || (session->side != NC_SERVER) || !nc_session_get_notif_status(session)) {
Radek Krejci93e80222016-10-03 13:34:25 +02001479 ERRARG("session");
1480 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001481 } else if (!notif || !notif->ntf || !notif->eventtime) {
Radek Krejci93e80222016-10-03 13:34:25 +02001482 ERRARG("notif");
1483 return NC_MSG_ERROR;
1484 }
1485
Michal Vasko131120a2018-05-29 15:44:02 +02001486 /* we do not need RPC lock for this, IO lock will be acquired properly */
1487 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001488 if (ret != NC_MSG_NOTIF) {
Michal Vasko05532772021-06-03 12:12:38 +02001489 ERR(session, "Failed to write notification (%s).", nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001490 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001491
Michal Vasko131120a2018-05-29 15:44:02 +02001492 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001493}
1494
Michal Vaskof9467762023-03-28 09:02:08 +02001495/**
1496 * @brief Send a reply acquiring IO lock as needed.
1497 * Session RPC lock must be held!
1498 *
1499 * @param[in] session Session to use.
1500 * @param[in] io_timeout Timeout to use for acquiring IO lock.
1501 * @param[in] rpc RPC to sent.
1502 * @return 0 on success.
1503 * @return Bitmask of NC_PSPOLL_ERROR (any fatal error) and NC_PSPOLL_REPLY_ERROR (reply failed to be sent).
1504 * @return NC_PSPOLL_ERROR on other errors.
Michal Vasko71090fc2016-05-24 16:37:28 +02001505 */
1506static int
Michal Vasko93224072021-11-09 12:14:28 +01001507nc_server_send_reply_io(struct nc_session *session, int io_timeout, const struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001508{
1509 nc_rpc_clb clb;
1510 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001511 const struct lysc_node *rpc_act = NULL;
1512 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001513 int ret = 0;
1514 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001515
Michal Vasko4a827e52016-03-03 10:59:00 +01001516 if (!rpc) {
1517 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001518 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001519 }
1520
Michal Vasko77367452021-02-16 16:32:18 +01001521 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001522 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001523 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001524 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001525 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001526 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001527 if (elem->schema->nodetype == LYS_ACTION) {
1528 rpc_act = elem->schema;
1529 break;
1530 }
Michal Vasko77367452021-02-16 16:32:18 +01001531 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001532 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001533 if (!rpc_act) {
1534 ERRINT;
1535 return NC_PSPOLL_ERROR;
1536 }
1537 }
1538
1539 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001540 if (!global_rpc_clb) {
1541 /* no callback, reply with a not-implemented error */
Michal Vasko93224072021-11-09 12:14:28 +01001542 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 +03001543 } else {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001544 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001545 }
Michal Vasko428087d2016-01-14 16:04:28 +01001546 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001547 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001548 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001549 }
1550
1551 if (!reply) {
Michal Vasko93224072021-11-09 12:14:28 +01001552 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001553 }
Michal Vasko77367452021-02-16 16:32:18 +01001554 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001555 if (reply->type == NC_RPL_ERROR) {
1556 ret |= NC_PSPOLL_REPLY_ERROR;
1557 }
1558 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001559
Michal Vasko131120a2018-05-29 15:44:02 +02001560 if (r != NC_MSG_REPLY) {
Michal Vasko15469492021-06-09 08:40:48 +02001561 ERR(session, "Failed to write reply (%s).", nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001562 ret |= NC_PSPOLL_ERROR;
1563 }
Michal Vasko428087d2016-01-14 16:04:28 +01001564
1565 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1566 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1567 session->status = NC_STATUS_INVALID;
1568 }
1569
Michal Vasko71090fc2016-05-24 16:37:28 +02001570 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001571}
1572
Michal Vaskof9467762023-03-28 09:02:08 +02001573/**
1574 * @brief Poll a session from pspoll acquiring IO lock as needed.
1575 * Session must be running and session RPC lock held!
1576 *
1577 * @param[in] session Session to use.
1578 * @param[in] io_timeout Timeout to use for acquiring IO lock.
1579 * @param[in] now_mono Current monotonic timestamp.
1580 * @param[in,out] msg Message to fill in case of an error.
1581 * @return NC_PSPOLL_RPC if some application data are available.
1582 * @return NC_PSPOLL_TIMEOUT if a timeout elapsed.
1583 * @return NC_PSPOLL_SSH_CHANNEL if a new SSH channel has been created.
1584 * @return NC_PSPOLL_SSH_MSG if just an SSH message has been processed.
1585 * @return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR if session has been terminated (@p msg filled).
1586 * @return NC_PSPOLL_ERROR on other fatal errors (@p msg filled).
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001587 */
1588static int
Michal Vasko131120a2018-05-29 15:44:02 +02001589nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001590{
Michal Vasko9a327362017-01-11 11:31:46 +01001591 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001592 int r, ret = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001593
Michal Vasko9a327362017-01-11 11:31:46 +01001594#ifdef NC_ENABLED_SSH
1595 struct nc_session *new;
1596#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001597
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001598 /* check timeout first */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001599 if (!(session->flags & NC_SESSION_CALLHOME) && !nc_session_get_notif_status(session) && server_opts.idle_timeout &&
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001600 (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001601 sprintf(msg, "session idle timeout elapsed");
1602 session->status = NC_STATUS_INVALID;
1603 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1604 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1605 }
1606
Michal Vasko131120a2018-05-29 15:44:02 +02001607 r = nc_session_io_lock(session, io_timeout, __func__);
1608 if (r < 0) {
1609 sprintf(msg, "session IO lock failed to be acquired");
1610 return NC_PSPOLL_ERROR;
1611 } else if (!r) {
1612 return NC_PSPOLL_TIMEOUT;
1613 }
1614
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001615 switch (session->ti_type) {
1616#ifdef NC_ENABLED_SSH
1617 case NC_TI_LIBSSH:
1618 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001619 if (r == SSH_EOF) {
1620 sprintf(msg, "SSH channel unexpected EOF");
1621 session->status = NC_STATUS_INVALID;
1622 session->term_reason = NC_SESSION_TERM_DROPPED;
1623 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1624 } else if (r == SSH_ERROR) {
1625 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001626 session->status = NC_STATUS_INVALID;
1627 session->term_reason = NC_SESSION_TERM_OTHER;
1628 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001629 } else if (!r) {
1630 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1631 /* new SSH message */
1632 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1633 if (session->ti.libssh.next) {
1634 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001635 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
1636 (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko8dcaa882017-10-19 14:28:42 +02001637 /* new NETCONF SSH channel */
1638 ret = NC_PSPOLL_SSH_CHANNEL;
1639 break;
1640 }
1641 }
1642 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001643 break;
1644 }
1645 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001646
Michal Vasko8dcaa882017-10-19 14:28:42 +02001647 /* just some SSH message */
1648 ret = NC_PSPOLL_SSH_MSG;
1649 } else {
1650 ret = NC_PSPOLL_TIMEOUT;
1651 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001652 } else {
1653 /* we have some application data */
1654 ret = NC_PSPOLL_RPC;
1655 }
1656 break;
1657#endif
1658#ifdef NC_ENABLED_TLS
1659 case NC_TI_OPENSSL:
1660 r = SSL_pending(session->ti.tls);
1661 if (!r) {
1662 /* no data pending in the SSL buffer, poll fd */
1663 pfd.fd = SSL_get_rfd(session->ti.tls);
1664 if (pfd.fd < 0) {
1665 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1666 ret = NC_PSPOLL_ERROR;
1667 break;
1668 }
1669 pfd.events = POLLIN;
1670 pfd.revents = 0;
1671 r = poll(&pfd, 1, 0);
1672
1673 if ((r < 0) && (errno != EINTR)) {
1674 sprintf(msg, "poll failed (%s)", strerror(errno));
1675 session->status = NC_STATUS_INVALID;
1676 ret = NC_PSPOLL_ERROR;
1677 } else if (r > 0) {
1678 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1679 sprintf(msg, "communication socket unexpectedly closed");
1680 session->status = NC_STATUS_INVALID;
1681 session->term_reason = NC_SESSION_TERM_DROPPED;
1682 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1683 } else if (pfd.revents & POLLERR) {
1684 sprintf(msg, "communication socket error");
1685 session->status = NC_STATUS_INVALID;
1686 session->term_reason = NC_SESSION_TERM_OTHER;
1687 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1688 } else {
1689 ret = NC_PSPOLL_RPC;
1690 }
1691 } else {
1692 ret = NC_PSPOLL_TIMEOUT;
1693 }
1694 } else {
1695 ret = NC_PSPOLL_RPC;
1696 }
1697 break;
1698#endif
1699 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001700 case NC_TI_UNIX:
1701 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001702 pfd.events = POLLIN;
1703 pfd.revents = 0;
1704 r = poll(&pfd, 1, 0);
1705
1706 if ((r < 0) && (errno != EINTR)) {
1707 sprintf(msg, "poll failed (%s)", strerror(errno));
1708 session->status = NC_STATUS_INVALID;
1709 ret = NC_PSPOLL_ERROR;
1710 } else if (r > 0) {
1711 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1712 sprintf(msg, "communication socket unexpectedly closed");
1713 session->status = NC_STATUS_INVALID;
1714 session->term_reason = NC_SESSION_TERM_DROPPED;
1715 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1716 } else if (pfd.revents & POLLERR) {
1717 sprintf(msg, "communication socket error");
1718 session->status = NC_STATUS_INVALID;
1719 session->term_reason = NC_SESSION_TERM_OTHER;
1720 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1721 } else {
1722 ret = NC_PSPOLL_RPC;
1723 }
1724 } else {
1725 ret = NC_PSPOLL_TIMEOUT;
1726 }
1727 break;
1728 case NC_TI_NONE:
1729 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1730 ret = NC_PSPOLL_ERROR;
1731 break;
1732 }
1733
Michal Vasko131120a2018-05-29 15:44:02 +02001734 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001735 return ret;
1736}
1737
1738API int
1739nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1740{
Michal Vasko443faa02022-10-20 09:09:03 +02001741 int ret = NC_PSPOLL_ERROR, r;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001742 uint8_t q_id;
1743 uint16_t i, j;
1744 char msg[256];
1745 struct timespec ts_timeout, ts_cur;
1746 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001747 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001748 struct nc_server_rpc *rpc = NULL;
1749
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001750 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001751 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001752 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001753 }
1754
Michal Vaskoade892d2017-02-22 13:40:35 +01001755 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001756 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001757 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001758 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001759
Michal Vaskoade892d2017-02-22 13:40:35 +01001760 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001761 nc_ps_unlock(ps, q_id, __func__);
1762 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001763 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001764
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001765 /* fill timespecs */
Michal Vaskod8a74192023-02-06 15:51:50 +01001766 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko36c7be82017-02-22 13:37:59 +01001767 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001768 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001769 }
1770
1771 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001772 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001773 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001774 if (ps->last_event_session == ps->session_count - 1) {
1775 i = j = 0;
1776 } else {
1777 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001778 }
Michal Vasko9a327362017-01-11 11:31:46 +01001779 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001780 cur_ps_session = ps->sessions[i];
1781 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001782
Michal Vasko131120a2018-05-29 15:44:02 +02001783 /* SESSION RPC LOCK */
1784 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001785 if (r == -1) {
1786 ret = NC_PSPOLL_ERROR;
1787 } else if (r == 1) {
1788 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001789 switch (cur_ps_session->state) {
1790 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001791 if (cur_session->status == NC_STATUS_RUNNING) {
1792 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001793 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001794
Michal Vasko131120a2018-05-29 15:44:02 +02001795 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001796 switch (ret) {
1797 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001798 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001799 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001800 break;
1801 case NC_PSPOLL_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001802 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001803 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001804 break;
1805 case NC_PSPOLL_TIMEOUT:
1806#ifdef NC_ENABLED_SSH
1807 case NC_PSPOLL_SSH_CHANNEL:
1808 case NC_PSPOLL_SSH_MSG:
1809#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001810 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001811 break;
1812 case NC_PSPOLL_RPC:
1813 /* let's keep the state busy, we are not done with this session */
1814 break;
1815 }
1816 } else {
1817 /* session is not fine, let the caller know */
1818 ret = NC_PSPOLL_SESSION_TERM;
1819 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1820 ret |= NC_PSPOLL_SESSION_ERROR;
1821 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001822 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001823 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001824 break;
1825 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001826 /* it definitely should not be busy because we have the lock */
1827 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001828 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001829 break;
1830 case NC_PS_STATE_INVALID:
1831 /* we got it locked, but it will be freed, let it be */
1832 ret = NC_PSPOLL_TIMEOUT;
1833 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001834 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001835
Michal Vasko131120a2018-05-29 15:44:02 +02001836 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001837 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001838 /* SESSION RPC UNLOCK */
1839 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001840 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001841 } else {
1842 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001843 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001844 }
Michal Vasko428087d2016-01-14 16:04:28 +01001845
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001846 /* something happened */
1847 if (ret != NC_PSPOLL_TIMEOUT) {
1848 break;
1849 }
1850
Michal Vasko9a327362017-01-11 11:31:46 +01001851 if (i == ps->session_count - 1) {
1852 i = 0;
1853 } else {
1854 ++i;
1855 }
1856 } while (i != j);
1857
Michal Vaskoade892d2017-02-22 13:40:35 +01001858 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001859 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001860 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001861
Michal Vaskod8a74192023-02-06 15:51:50 +01001862 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001863 /* final timeout */
1864 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001865 }
Michal Vasko428087d2016-01-14 16:04:28 +01001866 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001867 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001868
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001869 /* do we want to return the session? */
1870 switch (ret) {
1871 case NC_PSPOLL_RPC:
1872 case NC_PSPOLL_SESSION_TERM:
1873 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1874#ifdef NC_ENABLED_SSH
1875 case NC_PSPOLL_SSH_CHANNEL:
1876 case NC_PSPOLL_SSH_MSG:
1877#endif
1878 if (session) {
1879 *session = cur_session;
1880 }
1881 ps->last_event_session = i;
1882 break;
1883 default:
1884 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001885 }
Michal Vasko428087d2016-01-14 16:04:28 +01001886
Michal Vaskoade892d2017-02-22 13:40:35 +01001887 /* PS UNLOCK */
1888 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001889
Michal Vasko131120a2018-05-29 15:44:02 +02001890 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001891 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001892 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001893 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1894 if (cur_session->status != NC_STATUS_RUNNING) {
1895 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001896 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001897 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001898 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001899 }
1900 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001901 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001902
Michal Vasko7f1ee932018-10-11 09:41:42 +02001903 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001904 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001905 if (cur_session->status != NC_STATUS_RUNNING) {
1906 ret |= NC_PSPOLL_SESSION_TERM;
1907 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1908 ret |= NC_PSPOLL_SESSION_ERROR;
1909 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001910 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001911 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001912 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001913 }
Michal Vasko428087d2016-01-14 16:04:28 +01001914 }
Michal Vasko77367452021-02-16 16:32:18 +01001915 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001916
Michal Vasko131120a2018-05-29 15:44:02 +02001917 /* SESSION RPC UNLOCK */
1918 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001919 }
1920
Michal Vasko48a63ed2016-03-01 09:48:21 +01001921 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001922}
1923
Michal Vaskod09eae62016-02-01 10:32:52 +01001924API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001925nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001926{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001927 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001928 uint16_t i;
1929 struct nc_session *session;
1930
Michal Vasko9a25e932016-02-01 10:36:42 +01001931 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001932 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001933 return;
1934 }
1935
Michal Vasko48a63ed2016-03-01 09:48:21 +01001936 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001937 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001938 return;
1939 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001940
Michal Vasko48a63ed2016-03-01 09:48:21 +01001941 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001942 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001943 nc_session_free(ps->sessions[i]->session, data_free);
1944 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001945 }
1946 free(ps->sessions);
1947 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001948 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001949 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001950 } else {
1951 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001952 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1953 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001954 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001955 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001956 continue;
1957 }
1958
1959 ++i;
1960 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001961 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001962
1963 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001964 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001965}
1966
Michal Vasko5f352c52019-07-10 16:12:06 +02001967static int
apropp-molex4e903c32020-04-20 03:06:58 -04001968nc_get_uid(int sock, uid_t *uid)
1969{
Michal Vaskod3910912020-04-20 09:12:49 +02001970 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04001971
Michal Vaskod3910912020-04-20 09:12:49 +02001972#ifdef SO_PEERCRED
1973 struct ucred ucred;
1974 socklen_t len;
Michal Vasko292c5542023-02-01 14:33:17 +01001975
Michal Vaskod3910912020-04-20 09:12:49 +02001976 len = sizeof(ucred);
1977 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
1978 if (!ret) {
1979 *uid = ucred.uid;
1980 }
1981#else
1982 ret = getpeereid(sock, uid, NULL);
1983#endif
1984
1985 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001986 ERR(NULL, "Failed to get credentials from unix socket (%s).", strerror(errno));
Michal Vaskod3910912020-04-20 09:12:49 +02001987 return -1;
1988 }
apropp-molex4e903c32020-04-20 03:06:58 -04001989 return 0;
1990}
1991
1992static int
Michal Vasko5f352c52019-07-10 16:12:06 +02001993nc_accept_unix(struct nc_session *session, int sock)
1994{
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001995#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001996 struct passwd *pw, pw_buf;
Michal Vasko5f352c52019-07-10 16:12:06 +02001997 char *username;
Michal Vasko292c5542023-02-01 14:33:17 +01001998
Michal Vasko5f352c52019-07-10 16:12:06 +02001999 session->ti_type = NC_TI_UNIX;
Michal Vasko143aa142021-10-01 15:31:48 +02002000 uid_t uid = 0;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02002001 char *buf = NULL;
2002 size_t buf_len = 0;
Michal Vasko5f352c52019-07-10 16:12:06 +02002003
Michal Vaskod3910912020-04-20 09:12:49 +02002004 if (nc_get_uid(sock, &uid)) {
2005 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02002006 return -1;
2007 }
2008
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02002009 pw = nc_getpwuid(uid, &pw_buf, &buf, &buf_len);
Michal Vasko5f352c52019-07-10 16:12:06 +02002010 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02002011 ERR(NULL, "Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02002012 close(sock);
2013 return -1;
2014 }
2015
2016 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02002017 free(buf);
Michal Vasko5f352c52019-07-10 16:12:06 +02002018 if (username == NULL) {
2019 ERRMEM;
2020 close(sock);
2021 return -1;
2022 }
Michal Vasko93224072021-11-09 12:14:28 +01002023 session->username = username;
Michal Vasko5f352c52019-07-10 16:12:06 +02002024
2025 session->ti.unixsock.sock = sock;
2026
2027 return 1;
Claus Klein22091912020-01-20 13:45:47 +01002028#else
2029 return -1;
2030#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02002031}
2032
Michal Vaskoe2713da2016-08-22 16:06:40 +02002033API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002034nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01002035{
Michal Vasko3031aae2016-01-27 16:07:18 +01002036 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01002037 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01002038
Michal Vasko45e53ae2016-04-07 11:46:03 +02002039 if (!name) {
2040 ERRARG("name");
2041 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01002042 }
2043
Michal Vaskoade892d2017-02-22 13:40:35 +01002044 /* BIND LOCK */
2045 pthread_mutex_lock(&server_opts.bind_lock);
2046
2047 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002048 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002049
2050 /* check name uniqueness */
2051 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002052 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002053 ERR(NULL, "Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01002054 ret = -1;
2055 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01002056 }
2057 }
2058
Michal Vasko93224072021-11-09 12:14:28 +01002059 server_opts.endpts = nc_realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002060 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01002061 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002062 ret = -1;
2063 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002064 }
Michal Vasko93224072021-11-09 12:14:28 +01002065 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
2066 ++server_opts.endpt_count;
2067
2068 server_opts.endpts[server_opts.endpt_count - 1].name = strdup(name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002069 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002070 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
2071 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
2072 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002073
Michal Vaskoe2713da2016-08-22 16:06:40 +02002074 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002075 if (!server_opts.binds) {
2076 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002077 ret = -1;
2078 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01002079 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002080
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002081 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002082 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
2083
2084 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002085#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002086 case NC_TI_LIBSSH:
2087 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2088 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
2089 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002090 ret = -1;
2091 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002092 }
2093 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
roman41a11e42022-06-22 09:27:08 +02002094 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002095 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002096 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002097 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002098#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02002099#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002100 case NC_TI_OPENSSL:
2101 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2102 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
2103 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002104 ret = -1;
2105 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002106 }
2107 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002108#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002109 case NC_TI_UNIX:
2110 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
2111 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
2112 ERRMEM;
2113 ret = -1;
2114 goto cleanup;
2115 }
2116 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
2117 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
2118 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
2119 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002120 default:
2121 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01002122 ret = -1;
2123 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002124 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002125
Michal Vaskoade892d2017-02-22 13:40:35 +01002126cleanup:
2127 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002128 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002129
Michal Vaskoade892d2017-02-22 13:40:35 +01002130 /* BIND UNLOCK */
2131 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002132
Michal Vaskoade892d2017-02-22 13:40:35 +01002133 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002134}
2135
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002136API int
Michal Vasko59050372016-11-22 14:33:55 +01002137nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01002138{
2139 uint32_t i;
2140 int ret = -1;
2141
Michal Vaskoade892d2017-02-22 13:40:35 +01002142 /* BIND LOCK */
2143 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002144
Michal Vaskoade892d2017-02-22 13:40:35 +01002145 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002146 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002147
Michal Vasko59050372016-11-22 14:33:55 +01002148 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002149 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01002150 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002151 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002152 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002153#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002154 case NC_TI_LIBSSH:
2155 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2156 free(server_opts.endpts[i].opts.ssh);
2157 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002158#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002159#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002160 case NC_TI_OPENSSL:
2161 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2162 free(server_opts.endpts[i].opts.tls);
2163 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002164#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002165 case NC_TI_UNIX:
2166 free(server_opts.endpts[i].opts.unixsock);
2167 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002168 default:
2169 ERRINT;
2170 /* won't get here ...*/
2171 break;
2172 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002173 ret = 0;
2174 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002175 free(server_opts.endpts);
2176 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002177
2178 /* remove all binds */
2179 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002180 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002181 if (server_opts.binds[i].sock > -1) {
2182 close(server_opts.binds[i].sock);
2183 }
2184 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002185 free(server_opts.binds);
2186 server_opts.binds = NULL;
2187
Michal Vasko3031aae2016-01-27 16:07:18 +01002188 server_opts.endpt_count = 0;
2189
Michal Vasko1a38c862016-01-15 15:50:07 +01002190 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002191 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002192 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002193 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002194 /* remove endpt */
Michal Vasko93224072021-11-09 12:14:28 +01002195 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002196 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002197#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002198 case NC_TI_LIBSSH:
2199 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2200 free(server_opts.endpts[i].opts.ssh);
2201 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002202#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002203#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002204 case NC_TI_OPENSSL:
2205 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2206 free(server_opts.endpts[i].opts.tls);
2207 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002208#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002209 case NC_TI_UNIX:
2210 free(server_opts.endpts[i].opts.unixsock);
2211 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002212 default:
2213 ERRINT;
2214 break;
2215 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002216
Michal Vaskoe2713da2016-08-22 16:06:40 +02002217 /* remove bind(s) */
Michal Vasko93224072021-11-09 12:14:28 +01002218 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002219 if (server_opts.binds[i].sock > -1) {
2220 close(server_opts.binds[i].sock);
2221 }
2222
2223 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002224 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002225 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002226 free(server_opts.binds);
2227 server_opts.binds = NULL;
2228 free(server_opts.endpts);
2229 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002230 } else if (i < server_opts.endpt_count) {
2231 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2232 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002233 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002234
2235 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002236 if (name) {
2237 break;
2238 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002239 }
2240 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002241 }
2242
Michal Vaskoade892d2017-02-22 13:40:35 +01002243 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002244 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002245
Michal Vaskoade892d2017-02-22 13:40:35 +01002246 /* BIND UNLOCK */
2247 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002248
2249 return ret;
2250}
2251
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002252API int
2253nc_server_endpt_count(void)
2254{
2255 return server_opts.endpt_count;
2256}
2257
Michal Vasko1b5973e2020-01-30 16:05:46 +01002258API int
2259nc_server_is_endpt(const char *name)
2260{
2261 uint16_t i;
2262 int found = 0;
2263
Michal Vaskofb1724b2020-01-31 11:02:00 +01002264 if (!name) {
2265 return found;
2266 }
2267
Michal Vasko1b5973e2020-01-30 16:05:46 +01002268 /* ENDPT READ LOCK */
2269 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2270
2271 /* check name uniqueness */
2272 for (i = 0; i < server_opts.endpt_count; ++i) {
2273 if (!strcmp(server_opts.endpts[i].name, name)) {
2274 found = 1;
2275 break;
2276 }
2277 }
2278
2279 /* ENDPT UNLOCK */
2280 pthread_rwlock_unlock(&server_opts.endpt_lock);
2281
2282 return found;
2283}
2284
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002285int
2286nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2287{
2288 struct nc_endpt *endpt;
2289 struct nc_bind *bind = NULL;
2290 uint16_t i;
2291 int sock = -1, set_addr, ret = 0;
2292
2293 if (!endpt_name) {
2294 ERRARG("endpt_name");
2295 return -1;
2296 } else if ((!address && !port) || (address && port)) {
2297 ERRARG("address and port");
2298 return -1;
2299 }
2300
2301 if (address) {
2302 set_addr = 1;
2303 } else {
2304 set_addr = 0;
2305 }
2306
2307 /* BIND LOCK */
2308 pthread_mutex_lock(&server_opts.bind_lock);
2309
2310 /* ENDPT LOCK */
2311 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2312 if (!endpt) {
2313 /* BIND UNLOCK */
2314 pthread_mutex_unlock(&server_opts.bind_lock);
2315 return -1;
2316 }
2317
2318 bind = &server_opts.binds[i];
2319
2320 if (set_addr) {
2321 port = bind->port;
2322 } else {
2323 address = bind->address;
2324 }
2325
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002326 if (!set_addr && (endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002327 ret = -1;
2328 goto cleanup;
2329 }
2330
2331 /* we have all the information we need to create a listening socket */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002332 if (address && (port || (endpt->ti == NC_TI_UNIX))) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002333 /* create new socket, close the old one */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002334 if (endpt->ti == NC_TI_UNIX) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002335 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002336 } else {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002337 sock = nc_sock_listen_inet(address, port, &endpt->ka);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002338 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002339 if (sock == -1) {
2340 ret = -1;
2341 goto cleanup;
2342 }
2343
2344 if (bind->sock > -1) {
2345 close(bind->sock);
2346 }
2347 bind->sock = sock;
2348 } /* else we are just setting address or port */
2349
2350 if (set_addr) {
Michal Vasko93224072021-11-09 12:14:28 +01002351 free(bind->address);
2352 bind->address = strdup(address);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002353 } else {
2354 bind->port = port;
2355 }
2356
2357 if (sock > -1) {
Michal Vasko946cacb2020-08-12 11:18:08 +02002358 switch (endpt->ti) {
2359 case NC_TI_UNIX:
Michal Vasko05532772021-06-03 12:12:38 +02002360 VRB(NULL, "Listening on %s for UNIX connections.", address);
Michal Vasko946cacb2020-08-12 11:18:08 +02002361 break;
2362#ifdef NC_ENABLED_SSH
2363 case NC_TI_LIBSSH:
Michal Vasko05532772021-06-03 12:12:38 +02002364 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002365 break;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002366#endif
Michal Vasko946cacb2020-08-12 11:18:08 +02002367#ifdef NC_ENABLED_TLS
2368 case NC_TI_OPENSSL:
Michal Vasko05532772021-06-03 12:12:38 +02002369 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002370 break;
2371#endif
2372 default:
2373 ERRINT;
2374 break;
2375 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002376 }
2377
2378cleanup:
2379 /* ENDPT UNLOCK */
2380 pthread_rwlock_unlock(&server_opts.endpt_lock);
2381
2382 /* BIND UNLOCK */
2383 pthread_mutex_unlock(&server_opts.bind_lock);
2384
2385 return ret;
2386}
2387
2388API int
2389nc_server_endpt_set_address(const char *endpt_name, const char *address)
2390{
2391 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2392}
2393
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002394#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002395
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002396API int
2397nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2398{
2399 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2400}
2401
Michal Vasko946cacb2020-08-12 11:18:08 +02002402#endif
2403
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002404API int
2405nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2406{
2407 struct nc_endpt *endpt;
2408 uint16_t i;
2409 int ret = 0;
2410
2411 if (!endpt_name) {
2412 ERRARG("endpt_name");
2413 return -1;
2414 } else if (mode == 0) {
2415 ERRARG("mode");
2416 return -1;
2417 }
2418
2419 /* ENDPT LOCK */
2420 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002421 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002422 return -1;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002423 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002424
2425 if (endpt->ti != NC_TI_UNIX) {
2426 ret = -1;
2427 goto cleanup;
2428 }
2429
2430 endpt->opts.unixsock->mode = mode;
2431 endpt->opts.unixsock->uid = uid;
2432 endpt->opts.unixsock->gid = gid;
2433
2434cleanup:
2435 /* ENDPT UNLOCK */
2436 pthread_rwlock_unlock(&server_opts.endpt_lock);
2437
2438 return ret;
2439}
2440
2441API int
2442nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2443{
2444 struct nc_endpt *endpt;
2445 int ret = 0;
2446
2447 if (!endpt_name) {
2448 ERRARG("endpt_name");
2449 return -1;
2450 }
2451
2452 /* ENDPT LOCK */
2453 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2454 if (!endpt) {
2455 return -1;
2456 }
2457
2458 endpt->ka.enabled = (enable ? 1 : 0);
2459
2460 /* ENDPT UNLOCK */
2461 pthread_rwlock_unlock(&server_opts.endpt_lock);
2462
2463 return ret;
2464}
2465
2466API int
2467nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2468{
2469 struct nc_endpt *endpt;
2470 int ret = 0;
2471
2472 if (!endpt_name) {
2473 ERRARG("endpt_name");
2474 return -1;
2475 }
2476
2477 /* ENDPT LOCK */
2478 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2479 if (!endpt) {
2480 return -1;
2481 }
2482
2483 if (idle_time > -1) {
2484 endpt->ka.idle_time = idle_time;
2485 }
2486 if (max_probes > -1) {
2487 endpt->ka.max_probes = max_probes;
2488 }
2489 if (probe_interval > -1) {
2490 endpt->ka.probe_interval = probe_interval;
2491 }
2492
2493 /* ENDPT UNLOCK */
2494 pthread_rwlock_unlock(&server_opts.endpt_lock);
2495
2496 return ret;
2497}
2498
Michal Vasko71090fc2016-05-24 16:37:28 +02002499API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +01002500nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002501{
Michal Vasko71090fc2016-05-24 16:37:28 +02002502 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002503 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002504 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002505 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002506 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002507
Michal Vasko93224072021-11-09 12:14:28 +01002508 if (!ctx) {
2509 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +02002510 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002511 } else if (!session) {
2512 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002513 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002514 }
2515
Michal Vasko93224072021-11-09 12:14:28 +01002516 /* init ctx as needed */
2517 nc_server_init_ctx(ctx);
2518
Michal Vaskoade892d2017-02-22 13:40:35 +01002519 /* BIND LOCK */
2520 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002521
2522 if (!server_opts.endpt_count) {
Michal Vasko05532772021-06-03 12:12:38 +02002523 ERR(NULL, "No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002524 /* BIND UNLOCK */
2525 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002526 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002527 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002528
Michal Vaskoe2713da2016-08-22 16:06:40 +02002529 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002530 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002531 /* BIND UNLOCK */
2532 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002533 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002534 if (!ret) {
2535 return NC_MSG_WOULDBLOCK;
2536 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002537 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002538 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002539
2540 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2541 /* ENDPT READ LOCK */
2542 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2543
2544 /* BIND UNLOCK */
2545 pthread_mutex_unlock(&server_opts.bind_lock);
2546
Michal Vaskob48aa812016-01-18 14:13:09 +01002547 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002548
Michal Vasko131120a2018-05-29 15:44:02 +02002549 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002550 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002551 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002552 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002553 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002554 msgtype = NC_MSG_ERROR;
2555 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002556 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002557 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01002558 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko1a38c862016-01-15 15:50:07 +01002559 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01002560 (*session)->host = host;
Michal Vasko1a38c862016-01-15 15:50:07 +01002561 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002562
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002563 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002564#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002565 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2566 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002567 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002568 if (ret < 0) {
2569 msgtype = NC_MSG_ERROR;
2570 goto cleanup;
2571 } else if (!ret) {
2572 msgtype = NC_MSG_WOULDBLOCK;
2573 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002574 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002575 } else
2576#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002577#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002578 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2579 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002580 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002581 if (ret < 0) {
2582 msgtype = NC_MSG_ERROR;
2583 goto cleanup;
2584 } else if (!ret) {
2585 msgtype = NC_MSG_WOULDBLOCK;
2586 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002587 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002588 } else
2589#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002590 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2591 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2592 ret = nc_accept_unix(*session, sock);
2593 if (ret < 0) {
2594 msgtype = NC_MSG_ERROR;
2595 goto cleanup;
2596 }
2597 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002598 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002599 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002600 msgtype = NC_MSG_ERROR;
2601 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002602 }
2603
Michal Vasko2cc4c682016-03-01 09:16:48 +01002604 (*session)->data = NULL;
2605
Michal Vaskoade892d2017-02-22 13:40:35 +01002606 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002607 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002608
Michal Vaskob48aa812016-01-18 14:13:09 +01002609 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002610 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002611
Michal Vasko9e036d52016-01-08 10:49:26 +01002612 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002613 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002614 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002615 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002616 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002617 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002618 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002619
Michal Vaskod8a74192023-02-06 15:51:50 +01002620 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02002621 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +01002622 nc_realtime_get(&ts_cur);
Michal Vasko9fb42272017-10-05 13:50:05 +02002623 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002624 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002625
Michal Vasko71090fc2016-05-24 16:37:28 +02002626 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002627
Michal Vasko71090fc2016-05-24 16:37:28 +02002628cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002629 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002630 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002631
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002632 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002633 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002634 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002635}
2636
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002637#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002638
Michal Vaskoadf30f02019-06-24 09:34:47 +02002639/* client is expected to be locked */
2640static int
2641_nc_server_ch_client_del_endpt(struct nc_ch_client *client, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002642{
2643 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002644 int ret = -1;
2645
2646 if (!endpt_name) {
2647 /* remove all endpoints */
2648 for (i = 0; i < client->ch_endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002649 free(client->ch_endpts[i].name);
2650 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002651 if (client->ch_endpts[i].sock_pending != -1) {
2652 close(client->ch_endpts[i].sock_pending);
2653 }
2654 switch (client->ch_endpts[i].ti) {
2655#ifdef NC_ENABLED_SSH
2656 case NC_TI_LIBSSH:
2657 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2658 free(client->ch_endpts[i].opts.ssh);
2659 break;
2660#endif
2661#ifdef NC_ENABLED_TLS
2662 case NC_TI_OPENSSL:
2663 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2664 free(client->ch_endpts[i].opts.tls);
2665 break;
2666#endif
2667 default:
2668 ERRINT;
2669 /* won't get here ...*/
2670 break;
2671 }
2672 }
2673 free(client->ch_endpts);
2674 client->ch_endpts = NULL;
2675 client->ch_endpt_count = 0;
2676
2677 ret = 0;
2678 } else {
2679 for (i = 0; i < client->ch_endpt_count; ++i) {
2680 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
Michal Vasko93224072021-11-09 12:14:28 +01002681 free(client->ch_endpts[i].name);
2682 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002683 if (client->ch_endpts[i].sock_pending != -1) {
2684 close(client->ch_endpts[i].sock_pending);
2685 }
2686 switch (client->ch_endpts[i].ti) {
2687#ifdef NC_ENABLED_SSH
2688 case NC_TI_LIBSSH:
2689 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2690 free(client->ch_endpts[i].opts.ssh);
2691 break;
2692#endif
2693#ifdef NC_ENABLED_TLS
2694 case NC_TI_OPENSSL:
2695 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2696 free(client->ch_endpts[i].opts.tls);
2697 break;
2698#endif
2699 default:
2700 ERRINT;
2701 /* won't get here ...*/
2702 break;
2703 }
2704
2705 /* move last endpoint to the empty space */
2706 --client->ch_endpt_count;
2707 if (i < client->ch_endpt_count) {
2708 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2709 } else if (!server_opts.ch_client_count) {
2710 free(server_opts.ch_clients);
2711 server_opts.ch_clients = NULL;
2712 }
2713
2714 ret = 0;
2715 break;
2716 }
2717 }
2718 }
2719
2720 return ret;
2721}
2722
2723API int
2724nc_server_ch_add_client(const char *name)
2725{
2726 uint16_t i;
2727 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002728
2729 if (!name) {
2730 ERRARG("name");
2731 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002732 }
2733
2734 /* WRITE LOCK */
2735 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2736
2737 /* check name uniqueness */
2738 for (i = 0; i < server_opts.ch_client_count; ++i) {
2739 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002740 ERR(NULL, "Call Home client \"%s\" already exists.", name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002741 /* WRITE UNLOCK */
2742 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2743 return -1;
2744 }
2745 }
2746
2747 ++server_opts.ch_client_count;
2748 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2749 if (!server_opts.ch_clients) {
2750 ERRMEM;
2751 /* WRITE UNLOCK */
2752 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2753 return -1;
2754 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002755 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002756
Michal Vasko93224072021-11-09 12:14:28 +01002757 client->name = strdup(name);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002758 client->id = ATOMIC_INC_RELAXED(server_opts.new_client_id);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002759 client->ch_endpts = NULL;
2760 client->ch_endpt_count = 0;
2761 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002762
Michal Vasko2e6defd2016-10-07 15:48:15 +02002763 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002764 client->start_with = NC_CH_FIRST_LISTED;
2765 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002766
Michal Vaskoadf30f02019-06-24 09:34:47 +02002767 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002768
2769 /* WRITE UNLOCK */
2770 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2771
2772 return 0;
2773}
2774
2775API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002776nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002777{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002778 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002779 int ret = -1;
2780
2781 /* WRITE LOCK */
2782 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2783
Michal Vaskoadf30f02019-06-24 09:34:47 +02002784 if (!name) {
2785 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002786 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002787 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002788
2789 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002790 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002791
2792 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002793 ret = 0;
2794 }
2795 free(server_opts.ch_clients);
2796 server_opts.ch_clients = NULL;
2797
2798 server_opts.ch_client_count = 0;
2799
2800 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002801 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002802 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002803 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko93224072021-11-09 12:14:28 +01002804 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002805
Michal Vasko2e6defd2016-10-07 15:48:15 +02002806 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002807 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002808
2809 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2810
2811 /* move last client and endpoint(s) to the empty space */
2812 --server_opts.ch_client_count;
2813 if (i < server_opts.ch_client_count) {
2814 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002815 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002816 } else if (!server_opts.ch_client_count) {
2817 free(server_opts.ch_clients);
2818 server_opts.ch_clients = NULL;
2819 }
2820
2821 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002822 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002823 }
2824 }
2825 }
2826
2827 /* WRITE UNLOCK */
2828 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2829
2830 return ret;
2831}
2832
2833API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002834nc_server_ch_is_client(const char *name)
2835{
2836 uint16_t i;
2837 int found = 0;
2838
2839 if (!name) {
2840 return found;
2841 }
2842
2843 /* READ LOCK */
2844 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2845
2846 /* check name uniqueness */
2847 for (i = 0; i < server_opts.ch_client_count; ++i) {
2848 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2849 found = 1;
2850 break;
2851 }
2852 }
2853
2854 /* UNLOCK */
2855 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2856
2857 return found;
2858}
2859
2860API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002861nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002862{
2863 uint16_t i;
2864 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002865 struct nc_ch_endpt *endpt;
2866 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002867
2868 if (!client_name) {
2869 ERRARG("client_name");
2870 return -1;
2871 } else if (!endpt_name) {
2872 ERRARG("endpt_name");
2873 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002874 } else if (!ti) {
2875 ERRARG("ti");
2876 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002877 }
2878
2879 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002880 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002881 if (!client) {
2882 return -1;
2883 }
2884
2885 for (i = 0; i < client->ch_endpt_count; ++i) {
2886 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002887 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002888 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002889 }
2890 }
2891
2892 ++client->ch_endpt_count;
2893 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2894 if (!client->ch_endpts) {
2895 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002896 goto cleanup;
2897 }
2898 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2899
2900 memset(endpt, 0, sizeof *client->ch_endpts);
Michal Vasko93224072021-11-09 12:14:28 +01002901 endpt->name = strdup(endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002902 endpt->ti = ti;
2903 endpt->sock_pending = -1;
2904 endpt->ka.idle_time = 1;
2905 endpt->ka.max_probes = 10;
2906 endpt->ka.probe_interval = 5;
2907
2908 switch (ti) {
2909#ifdef NC_ENABLED_SSH
2910 case NC_TI_LIBSSH:
2911 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2912 if (!endpt->opts.ssh) {
2913 ERRMEM;
2914 goto cleanup;
2915 }
roman41a11e42022-06-22 09:27:08 +02002916 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002917 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002918 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002919 break;
2920#endif
2921#ifdef NC_ENABLED_TLS
2922 case NC_TI_OPENSSL:
2923 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2924 if (!endpt->opts.tls) {
2925 ERRMEM;
2926 goto cleanup;
2927 }
2928 break;
2929#endif
2930 default:
2931 ERRINT;
2932 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002933 }
2934
Michal Vaskoadf30f02019-06-24 09:34:47 +02002935 /* success */
2936 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002937
Michal Vaskoadf30f02019-06-24 09:34:47 +02002938cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002939 /* UNLOCK */
2940 nc_server_ch_client_unlock(client);
2941
Michal Vaskoadf30f02019-06-24 09:34:47 +02002942 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002943}
2944
2945API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002946nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002947{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002948 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002949 struct nc_ch_client *client;
2950
2951 if (!client_name) {
2952 ERRARG("client_name");
2953 return -1;
2954 }
2955
2956 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002957 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002958 if (!client) {
2959 return -1;
2960 }
2961
Michal Vaskoadf30f02019-06-24 09:34:47 +02002962 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002963
2964 /* UNLOCK */
2965 nc_server_ch_client_unlock(client);
2966
2967 return ret;
2968}
2969
2970API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002971nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2972{
2973 uint16_t i;
2974 struct nc_ch_client *client = NULL;
2975 int found = 0;
2976
2977 if (!client_name || !endpt_name) {
2978 return found;
2979 }
2980
2981 /* READ LOCK */
2982 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2983
2984 for (i = 0; i < server_opts.ch_client_count; ++i) {
2985 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2986 client = &server_opts.ch_clients[i];
2987 break;
2988 }
2989 }
2990
2991 if (!client) {
2992 goto cleanup;
2993 }
2994
2995 for (i = 0; i < client->ch_endpt_count; ++i) {
2996 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2997 found = 1;
2998 break;
2999 }
3000 }
3001
3002cleanup:
3003 /* UNLOCK */
3004 pthread_rwlock_unlock(&server_opts.ch_client_lock);
3005 return found;
3006}
3007
3008API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003009nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
3010{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003011 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003012 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003013
3014 if (!client_name) {
3015 ERRARG("client_name");
3016 return -1;
3017 } else if (!endpt_name) {
3018 ERRARG("endpt_name");
3019 return -1;
3020 } else if (!address) {
3021 ERRARG("address");
3022 return -1;
3023 }
3024
3025 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003026 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3027 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003028 return -1;
3029 }
3030
Michal Vasko93224072021-11-09 12:14:28 +01003031 free(endpt->address);
3032 endpt->address = strdup(address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003033
3034 /* UNLOCK */
3035 nc_server_ch_client_unlock(client);
3036
Michal Vaskoadf30f02019-06-24 09:34:47 +02003037 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003038}
3039
3040API int
3041nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
3042{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003043 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003044 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003045
3046 if (!client_name) {
3047 ERRARG("client_name");
3048 return -1;
3049 } else if (!endpt_name) {
3050 ERRARG("endpt_name");
3051 return -1;
3052 } else if (!port) {
3053 ERRARG("port");
3054 return -1;
3055 }
3056
3057 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003058 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3059 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003060 return -1;
3061 }
3062
Michal Vaskoadf30f02019-06-24 09:34:47 +02003063 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003064
3065 /* UNLOCK */
3066 nc_server_ch_client_unlock(client);
3067
ravsz5c5a4422020-03-31 15:53:21 +02003068 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003069}
3070
3071API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003072nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
3073{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003074 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003075 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003076
3077 if (!client_name) {
3078 ERRARG("client_name");
3079 return -1;
3080 } else if (!endpt_name) {
3081 ERRARG("endpt_name");
3082 return -1;
3083 }
3084
3085 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003086 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3087 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003088 return -1;
3089 }
3090
Michal Vaskoadf30f02019-06-24 09:34:47 +02003091 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003092
3093 /* UNLOCK */
3094 nc_server_ch_client_unlock(client);
3095
Michal Vasko9af829a2019-09-12 13:50:00 +02003096 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003097}
3098
3099API int
3100nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
3101 int probe_interval)
3102{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003103 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003104 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003105
3106 if (!client_name) {
3107 ERRARG("client_name");
3108 return -1;
3109 } else if (!endpt_name) {
3110 ERRARG("endpt_name");
3111 return -1;
3112 }
3113
3114 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003115 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3116 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003117 return -1;
3118 }
3119
Michal Vaskoadf30f02019-06-24 09:34:47 +02003120 if (idle_time > -1) {
3121 endpt->ka.idle_time = idle_time;
3122 }
3123 if (max_probes > -1) {
3124 endpt->ka.max_probes = max_probes;
3125 }
3126 if (probe_interval > -1) {
3127 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003128 }
3129
3130 /* UNLOCK */
3131 nc_server_ch_client_unlock(client);
3132
Michal Vasko9af829a2019-09-12 13:50:00 +02003133 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003134}
3135
3136API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003137nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
3138{
3139 struct nc_ch_client *client;
3140
3141 if (!client_name) {
3142 ERRARG("client_name");
3143 return -1;
3144 } else if (!conn_type) {
3145 ERRARG("conn_type");
3146 return -1;
3147 }
3148
3149 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003150 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003151 if (!client) {
3152 return -1;
3153 }
3154
3155 if (client->conn_type != conn_type) {
3156 client->conn_type = conn_type;
3157
3158 /* set default options */
3159 switch (conn_type) {
3160 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003161 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003162 break;
3163 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003164 client->conn.period.period = 60;
3165 client->conn.period.anchor_time = 0;
3166 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003167 break;
3168 default:
3169 ERRINT;
3170 break;
3171 }
3172 }
3173
3174 /* UNLOCK */
3175 nc_server_ch_client_unlock(client);
3176
3177 return 0;
3178}
3179
3180API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003181nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
3182{
3183 struct nc_ch_client *client;
3184
3185 if (!client_name) {
3186 ERRARG("client_name");
3187 return -1;
3188 } else if (!period) {
3189 ERRARG("period");
3190 return -1;
3191 }
3192
3193 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003194 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003195 if (!client) {
3196 return -1;
3197 }
3198
3199 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003200 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003201 /* UNLOCK */
3202 nc_server_ch_client_unlock(client);
3203 return -1;
3204 }
3205
3206 client->conn.period.period = period;
3207
3208 /* UNLOCK */
3209 nc_server_ch_client_unlock(client);
3210
3211 return 0;
3212}
3213
3214API int
3215nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003216{
3217 struct nc_ch_client *client;
3218
3219 if (!client_name) {
3220 ERRARG("client_name");
3221 return -1;
3222 }
3223
3224 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003225 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003226 if (!client) {
3227 return -1;
3228 }
3229
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003230 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003231 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003232 /* UNLOCK */
3233 nc_server_ch_client_unlock(client);
3234 return -1;
3235 }
3236
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003237 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003238
3239 /* UNLOCK */
3240 nc_server_ch_client_unlock(client);
3241
3242 return 0;
3243}
3244
3245API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003246nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003247{
3248 struct nc_ch_client *client;
3249
3250 if (!client_name) {
3251 ERRARG("client_name");
3252 return -1;
3253 }
3254
3255 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003256 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003257 if (!client) {
3258 return -1;
3259 }
3260
3261 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003262 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003263 /* UNLOCK */
3264 nc_server_ch_client_unlock(client);
3265 return -1;
3266 }
3267
3268 client->conn.period.idle_timeout = idle_timeout;
3269
3270 /* UNLOCK */
3271 nc_server_ch_client_unlock(client);
3272
3273 return 0;
3274}
3275
3276API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003277nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
3278{
3279 struct nc_ch_client *client;
3280
3281 if (!client_name) {
3282 ERRARG("client_name");
3283 return -1;
3284 }
3285
3286 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003287 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003288 if (!client) {
3289 return -1;
3290 }
3291
3292 client->start_with = start_with;
3293
3294 /* UNLOCK */
3295 nc_server_ch_client_unlock(client);
3296
3297 return 0;
3298}
3299
3300API int
3301nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3302{
3303 struct nc_ch_client *client;
3304
3305 if (!client_name) {
3306 ERRARG("client_name");
3307 return -1;
3308 } else if (!max_attempts) {
3309 ERRARG("max_attempts");
3310 return -1;
3311 }
3312
3313 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003314 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003315 if (!client) {
3316 return -1;
3317 }
3318
3319 client->max_attempts = max_attempts;
3320
3321 /* UNLOCK */
3322 nc_server_ch_client_unlock(client);
3323
3324 return 0;
3325}
3326
Michal Vasko056f53c2022-10-21 13:38:15 +02003327/**
3328 * @brief Create a connection for an endpoint.
3329 *
3330 * Client lock is expected to be held.
3331 *
3332 * @param[in] endpt Endpoint to use.
3333 * @param[in] acquire_ctx_cb Callback for acquiring the libyang context.
3334 * @param[in] release_ctx_cb Callback for releasing the libyang context.
3335 * @param[in] ctx_cb_data Context callbacks data.
3336 * @param[out] session Created NC session.
3337 * @return NC_MSG values.
3338 */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003339static NC_MSG_TYPE
Michal Vasko58bac1c2022-03-24 15:25:26 +01003340nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
3341 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 +01003342{
Michal Vasko71090fc2016-05-24 16:37:28 +02003343 NC_MSG_TYPE msgtype;
Michal Vasko58bac1c2022-03-24 15:25:26 +01003344 const struct ly_ctx *ctx = NULL;
Michal Vaskob05053d2016-01-22 16:12:06 +01003345 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003346 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003347 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003348
Michal Vasko056f53c2022-10-21 13:38:15 +02003349 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 +01003350 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02003351 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003352 }
3353
Michal Vasko93224072021-11-09 12:14:28 +01003354 /* acquire context */
3355 ctx = acquire_ctx_cb(ctx_cb_data);
3356 if (!ctx) {
3357 ERR(NULL, "Failed to acquire context for a new Call Home session.");
3358 close(sock);
3359 free(ip_host);
3360 return NC_MSG_ERROR;
3361 }
3362
3363 /* create session */
Michal Vasko131120a2018-05-29 15:44:02 +02003364 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003365 if (!(*session)) {
3366 ERRMEM;
3367 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003368 free(ip_host);
Michal Vaskod530d362022-09-07 10:07:57 +02003369 msgtype = NC_MSG_ERROR;
3370 goto fail;
Michal Vaskob05053d2016-01-22 16:12:06 +01003371 }
3372 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01003373 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vaskodc96bb92023-03-28 08:52:48 +02003374 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vasko93224072021-11-09 12:14:28 +01003375 (*session)->host = ip_host;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003376 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003377
Michal Vaskob05053d2016-01-22 16:12:06 +01003378 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003379#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003380 if (endpt->ti == NC_TI_LIBSSH) {
3381 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003382 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003383 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003384
Michal Vasko71090fc2016-05-24 16:37:28 +02003385 if (ret < 0) {
3386 msgtype = NC_MSG_ERROR;
3387 goto fail;
3388 } else if (!ret) {
3389 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003390 goto fail;
3391 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003392 } else
3393#endif
Radek Krejci53691be2016-02-22 13:58:37 +01003394#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003395 if (endpt->ti == NC_TI_OPENSSL) {
3396 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003397 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003398 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003399
Michal Vasko71090fc2016-05-24 16:37:28 +02003400 if (ret < 0) {
3401 msgtype = NC_MSG_ERROR;
3402 goto fail;
3403 } else if (!ret) {
3404 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003405 goto fail;
3406 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003407 } else
3408#endif
3409 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003410 ERRINT;
3411 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003412 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003413 goto fail;
3414 }
3415
3416 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02003417 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003418
3419 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003420 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003421 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003422 goto fail;
3423 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003424
Michal Vaskod8a74192023-02-06 15:51:50 +01003425 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02003426 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +01003427 nc_realtime_get(&ts_cur);
Michal Vasko9fb42272017-10-05 13:50:05 +02003428 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003429 (*session)->status = NC_STATUS_RUNNING;
3430
Michal Vasko71090fc2016-05-24 16:37:28 +02003431 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003432
3433fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003434 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003435 *session = NULL;
Michal Vasko58bac1c2022-03-24 15:25:26 +01003436 if (ctx) {
3437 release_ctx_cb(ctx_cb_data);
3438 }
Michal Vasko71090fc2016-05-24 16:37:28 +02003439 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003440}
3441
Michal Vasko2e6defd2016-10-07 15:48:15 +02003442struct nc_ch_client_thread_arg {
3443 char *client_name;
Michal Vasko93224072021-11-09 12:14:28 +01003444 nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb;
3445 nc_server_ch_session_release_ctx_cb release_ctx_cb;
3446 void *ctx_cb_data;
3447 nc_server_ch_new_session_cb new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003448};
3449
3450static struct nc_ch_client *
3451nc_server_ch_client_with_endpt_lock(const char *name)
3452{
3453 struct nc_ch_client *client;
3454
3455 while (1) {
3456 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003457 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003458 if (!client) {
3459 return NULL;
3460 }
3461 if (client->ch_endpt_count) {
3462 return client;
3463 }
3464 /* no endpoints defined yet */
3465
3466 /* UNLOCK */
3467 nc_server_ch_client_unlock(client);
3468
3469 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3470 }
3471
3472 return NULL;
3473}
3474
3475static int
3476nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3477{
Michal Vasko3f05a092018-03-13 10:39:49 +01003478 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003479 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003480 struct timespec ts;
3481 struct nc_ch_client *client;
3482
Michal Vasko2e6defd2016-10-07 15:48:15 +02003483 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003484 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003485
Michal Vaskofeccb312022-03-24 15:24:59 +01003486 session->flags |= NC_SESSION_CH_THREAD;
Michal Vasko0db3db52021-03-03 10:45:42 +01003487
Michal Vasko2e6defd2016-10-07 15:48:15 +02003488 /* give the session to the user */
Michal Vasko93224072021-11-09 12:14:28 +01003489 if (data->new_session_cb(data->client_name, session)) {
Michal Vaskof1c26c22021-04-12 16:34:33 +02003490 /* something is wrong, free the session */
Michal Vaskofeccb312022-03-24 15:24:59 +01003491 session->flags &= ~NC_SESSION_CH_THREAD;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003492
3493 /* CH UNLOCK */
3494 pthread_mutex_unlock(&session->opts.server.ch_lock);
3495
Michal Vasko77d56d72022-09-07 10:30:48 +02003496 /* session terminated, free it and release its context */
Michal Vaskof1c26c22021-04-12 16:34:33 +02003497 nc_session_free(session, NULL);
Michal Vasko58bac1c2022-03-24 15:25:26 +01003498 data->release_ctx_cb(data->ctx_cb_data);
3499 return ret;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003500 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003501
3502 do {
Michal Vaskod8a74192023-02-06 15:51:50 +01003503 nc_timeouttime_get(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003504
Michal Vasko0db3db52021-03-03 10:45:42 +01003505 /* CH COND WAIT */
Michal Vaskod8a74192023-02-06 15:51:50 +01003506 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 +01003507 if (!r) {
3508 /* we were woken up, something probably happened */
3509 if (session->status != NC_STATUS_RUNNING) {
3510 break;
3511 }
3512 } else if (r != ETIMEDOUT) {
Michal Vasko05532772021-06-03 12:12:38 +02003513 ERR(session, "Pthread condition timedwait failed (%s).", strerror(r));
Michal Vasko3f05a092018-03-13 10:39:49 +01003514 ret = -1;
3515 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003516 }
3517
Michal Vasko2e6defd2016-10-07 15:48:15 +02003518 /* check whether the client was not removed */
3519 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003520 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003521 if (!client) {
3522 /* client was removed, finish thread */
Michal Vasko05532772021-06-03 12:12:38 +02003523 VRB(session, "Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003524 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003525 ret = 1;
3526 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003527 }
3528
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003529 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003530 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003531 } else {
3532 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003533 }
3534
Michal Vaskod8a74192023-02-06 15:51:50 +01003535 nc_timeouttime_get(&ts, 0);
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003536 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 +02003537 VRB(session, "Call Home client \"%s\": session idle timeout elapsed.", client->name);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003538 session->status = NC_STATUS_INVALID;
3539 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3540 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003541
3542 /* UNLOCK */
3543 nc_server_ch_client_unlock(client);
3544
3545 } while (session->status == NC_STATUS_RUNNING);
3546
Michal Vaskofeccb312022-03-24 15:24:59 +01003547 /* signal to nc_session_free() that CH thread is terminating */
3548 session->flags &= ~NC_SESSION_CH_THREAD;
3549 pthread_cond_signal(&session->opts.server.ch_cond);
Michal Vasko0db3db52021-03-03 10:45:42 +01003550
Michal Vasko27377422018-03-15 08:59:35 +01003551 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003552 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01003553
Michal Vasko3f05a092018-03-13 10:39:49 +01003554 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003555}
3556
3557static void *
3558nc_ch_client_thread(void *arg)
3559{
3560 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3561 NC_MSG_TYPE msgtype;
3562 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003563 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003564 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003565 struct nc_ch_endpt *cur_endpt;
3566 struct nc_session *session;
3567 struct nc_ch_client *client;
Michal Vasko5d0a1e32022-09-07 07:46:36 +02003568 uint32_t client_id, reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003569
3570 /* LOCK */
3571 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3572 if (!client) {
3573 goto cleanup;
3574 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003575 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003576
3577 cur_endpt = &client->ch_endpts[0];
3578 cur_endpt_name = strdup(cur_endpt->name);
3579
3580 while (1) {
Michal Vasko056f53c2022-10-21 13:38:15 +02003581 if (!cur_attempts) {
3582 VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" connecting...", data->client_name, cur_endpt_name);
3583 }
Michal Vasko58bac1c2022-03-24 15:25:26 +01003584 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 +02003585
3586 if (msgtype == NC_MSG_HELLO) {
3587 /* UNLOCK */
3588 nc_server_ch_client_unlock(client);
3589
Michal Vasko05532772021-06-03 12:12:38 +02003590 VRB(NULL, "Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003591 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3592 goto cleanup;
3593 }
Michal Vasko05532772021-06-03 12:12:38 +02003594 VRB(NULL, "Call Home client \"%s\" session terminated.", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003595
3596 /* LOCK */
3597 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3598 if (!client) {
3599 goto cleanup;
3600 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003601 if (client->id != client_id) {
3602 nc_server_ch_client_unlock(client);
3603 goto cleanup;
3604 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003605
3606 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003607 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003608 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko18e1fa02021-11-29 09:02:05 +01003609 if (client->conn.period.anchor_time) {
3610 /* anchored */
3611 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3612 } else {
3613 /* fixed timeout */
3614 reconnect_in = client->conn.period.period * 60;
3615 }
3616
Michal Vasko2e6defd2016-10-07 15:48:15 +02003617 /* UNLOCK */
3618 nc_server_ch_client_unlock(client);
3619
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003620 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
Michal Vasko75b836e2022-09-07 07:51:45 +02003621 VRB(NULL, "Call Home client \"%s\" reconnecting in %" PRIu32 " seconds.", data->client_name, reconnect_in);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003622 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003623
3624 /* LOCK */
3625 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3626 if (!client) {
3627 goto cleanup;
3628 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003629 if (client->id != client_id) {
3630 nc_server_ch_client_unlock(client);
3631 goto cleanup;
3632 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003633 }
3634
3635 /* set next endpoint to try */
3636 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003637 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003638 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003639 /* we keep the current one but due to unlock/lock we have to find it again */
3640 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3641 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3642 break;
3643 }
3644 }
3645 if (next_endpt_index >= client->ch_endpt_count) {
3646 /* endpoint was removed, start with the first one */
3647 next_endpt_index = 0;
3648 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003649 } else {
3650 /* just get a random index */
3651 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003652 }
3653
Michal Vasko2e6defd2016-10-07 15:48:15 +02003654 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003655 /* UNLOCK */
3656 nc_server_ch_client_unlock(client);
3657
Michal Vasko2e6defd2016-10-07 15:48:15 +02003658 /* session was not created */
Michal Vaskod8951c72022-09-26 09:39:29 +02003659 sleep(NC_CH_ENDPT_BACKOFF_WAIT);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003660
Michal Vasko6bb116b2016-10-26 13:53:46 +02003661 /* LOCK */
3662 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3663 if (!client) {
3664 goto cleanup;
3665 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003666 if (client->id != client_id) {
3667 nc_server_ch_client_unlock(client);
3668 goto cleanup;
3669 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003670
Michal Vasko2e6defd2016-10-07 15:48:15 +02003671 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003672
3673 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003674 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3675 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003676 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003677 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003678 }
3679
Peter Feiged05f2252018-09-03 08:09:47 +00003680 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003681 /* endpoint was removed, start with the first one */
Michal Vasko056f53c2022-10-21 13:38:15 +02003682 VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" removed.", data->client_name, cur_endpt_name);
Peter Feiged05f2252018-09-03 08:09:47 +00003683 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003684 cur_attempts = 0;
3685 } else if (cur_attempts == client->max_attempts) {
3686 /* we have tried to connect to this endpoint enough times */
Michal Vasko056f53c2022-10-21 13:38:15 +02003687 VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" failed connection attempt limit %" PRIu8 " reached.",
3688 data->client_name, cur_endpt_name, client->max_attempts);
3689
3690 /* clear a pending socket, if any */
3691 cur_endpt = &client->ch_endpts[next_endpt_index];
3692 if (cur_endpt->sock_pending > -1) {
3693 close(cur_endpt->sock_pending);
3694 cur_endpt->sock_pending = -1;
3695 }
3696
Peter Feiged05f2252018-09-03 08:09:47 +00003697 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003698 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003699 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003700 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003701 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003702 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003703 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003704 cur_attempts = 0;
3705 } /* else we keep the current one */
3706 }
Peter Feiged05f2252018-09-03 08:09:47 +00003707
3708 cur_endpt = &client->ch_endpts[next_endpt_index];
3709 free(cur_endpt_name);
3710 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003711 }
3712
3713cleanup:
Michal Vasko05532772021-06-03 12:12:38 +02003714 VRB(NULL, "Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003715 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003716 free(data->client_name);
3717 free(data);
3718 return NULL;
3719}
3720
3721API int
Michal Vasko93224072021-11-09 12:14:28 +01003722nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
3723 nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb)
Michal Vasko3f05a092018-03-13 10:39:49 +01003724{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003725 int ret;
3726 pthread_t tid;
3727 struct nc_ch_client_thread_arg *arg;
3728
3729 if (!client_name) {
3730 ERRARG("client_name");
3731 return -1;
Michal Vasko93224072021-11-09 12:14:28 +01003732 } else if (!acquire_ctx_cb) {
3733 ERRARG("acquire_ctx_cb");
3734 return -1;
3735 } else if (!release_ctx_cb) {
3736 ERRARG("release_ctx_cb");
3737 return -1;
3738 } else if (!new_session_cb) {
3739 ERRARG("new_session_cb");
Michal Vasko2e6defd2016-10-07 15:48:15 +02003740 return -1;
3741 }
3742
3743 arg = malloc(sizeof *arg);
3744 if (!arg) {
3745 ERRMEM;
3746 return -1;
3747 }
3748 arg->client_name = strdup(client_name);
3749 if (!arg->client_name) {
3750 ERRMEM;
3751 free(arg);
3752 return -1;
3753 }
Michal Vasko93224072021-11-09 12:14:28 +01003754 arg->acquire_ctx_cb = acquire_ctx_cb;
3755 arg->release_ctx_cb = release_ctx_cb;
3756 arg->ctx_cb_data = ctx_cb_data;
3757 arg->new_session_cb = new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003758
3759 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3760 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02003761 ERR(NULL, "Creating a new thread failed (%s).", strerror(ret));
Michal Vasko2e6defd2016-10-07 15:48:15 +02003762 free(arg->client_name);
3763 free(arg);
3764 return -1;
3765 }
3766 /* the thread now manages arg */
3767
3768 pthread_detach(tid);
3769
3770 return 0;
3771}
3772
Radek Krejci53691be2016-02-22 13:58:37 +01003773#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003774
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003775API time_t
3776nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003777{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003778 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003779 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003780 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003781 }
3782
Michal Vasko2e6defd2016-10-07 15:48:15 +02003783 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003784}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003785
3786API void
Michal Vasko71dbd772021-03-23 14:08:37 +01003787nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01003788{
3789 if (!session || (session->side != NC_SERVER)) {
3790 ERRARG("session");
3791 return;
3792 }
3793
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003794 /* NTF STATUS LOCK */
3795 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
3796
Michal Vasko71dbd772021-03-23 14:08:37 +01003797 ++session->opts.server.ntf_status;
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003798
3799 /* NTF STATUS UNLOCK */
3800 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko71dbd772021-03-23 14:08:37 +01003801}
3802
3803API void
3804nc_session_dec_notif_status(struct nc_session *session)
3805{
3806 if (!session || (session->side != NC_SERVER)) {
3807 ERRARG("session");
3808 return;
3809 }
3810
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003811 /* NTF STATUS LOCK */
3812 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
3813
Michal Vasko71dbd772021-03-23 14:08:37 +01003814 if (session->opts.server.ntf_status) {
3815 --session->opts.server.ntf_status;
3816 }
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003817
3818 /* NTF STATUS UNLOCK */
3819 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003820}
3821
3822API int
3823nc_session_get_notif_status(const struct nc_session *session)
3824{
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003825 uint32_t ntf_status;
3826
Michal Vasko3486a7c2017-03-03 13:28:07 +01003827 if (!session || (session->side != NC_SERVER)) {
3828 ERRARG("session");
3829 return 0;
3830 }
3831
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003832 /* NTF STATUS LOCK */
3833 pthread_mutex_lock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
3834
3835 ntf_status = session->opts.server.ntf_status;
3836
3837 /* NTF STATUS UNLOCK */
3838 pthread_mutex_unlock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
3839
3840 return ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003841}