blob: 03938ee752d9d8fa9b27631d853d241a886d118e [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"
roman466719d2023-05-05 16:14:37 +020038#include "config_new_ssh.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010039#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010040#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020041#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010042
Michal Vaskob48aa812016-01-18 14:13:09 +010043struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010044#ifdef NC_ENABLED_SSH
45 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
46#endif
47 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020048 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
49 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010050};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010051
fanchanghu966f2de2016-07-21 02:28:57 -040052static nc_rpc_clb global_rpc_clb = NULL;
53
Michal Vasko3031aae2016-01-27 16:07:18 +010054struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010055nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010056{
57 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010058 struct nc_endpt *endpt = NULL;
59
Michal Vaskoddce1212019-05-24 09:58:49 +020060 if (!name) {
61 ERRARG("endpt_name");
62 return NULL;
63 }
64
Michal Vaskoade892d2017-02-22 13:40:35 +010065 /* WRITE LOCK */
66 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010067
68 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020069 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010070 endpt = &server_opts.endpts[i];
71 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010072 }
73 }
74
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010075 if (!endpt) {
Michal Vasko05532772021-06-03 12:12:38 +020076 ERR(NULL, "Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010077 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020078 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010079 return NULL;
80 }
81
Michal Vaskoe2713da2016-08-22 16:06:40 +020082 if (idx) {
83 *idx = i;
84 }
85
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010086 return endpt;
87}
88
Michal Vaskoadf30f02019-06-24 09:34:47 +020089struct nc_ch_endpt *
90nc_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 +010091{
Michal Vaskoadf30f02019-06-24 09:34:47 +020092 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020093 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020094 struct nc_ch_endpt *endpt = NULL;
95
96 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +020097
Michal Vaskoddce1212019-05-24 09:58:49 +020098 if (!name) {
99 ERRARG("client_name");
100 return NULL;
101 }
102
Michal Vasko2e6defd2016-10-07 15:48:15 +0200103 /* READ LOCK */
104 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
105
106 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200107 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200108 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200109 if (!endpt_name && !ti) {
110 /* return only client */
111 break;
112 }
113 for (j = 0; j < client->ch_endpt_count; ++j) {
Michal Vasko530d95c2021-05-28 13:32:02 +0200114 if ((!endpt_name || !strcmp(client->ch_endpts[j].name, endpt_name)) &&
115 (!ti || (ti == client->ch_endpts[j].ti))) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200116 endpt = &client->ch_endpts[j];
117 break;
118 }
119 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200120 break;
121 }
122 }
123
124 if (!client) {
Michal Vasko05532772021-06-03 12:12:38 +0200125 ERR(NULL, "Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200126
Michal Vasko2e6defd2016-10-07 15:48:15 +0200127 /* READ UNLOCK */
128 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200129 } else if (endpt_name && ti && !endpt) {
Michal Vasko05532772021-06-03 12:12:38 +0200130 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200131
132 /* READ UNLOCK */
133 pthread_rwlock_unlock(&server_opts.ch_client_lock);
134 } else {
135 /* CH CLIENT LOCK */
136 pthread_mutex_lock(&client->lock);
137
138 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200139 }
140
Michal Vaskoadf30f02019-06-24 09:34:47 +0200141 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200142}
143
144void
145nc_server_ch_client_unlock(struct nc_ch_client *client)
146{
147 /* CH CLIENT UNLOCK */
148 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100149
150 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200151 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100152}
Michal Vasko086311b2016-01-08 09:53:11 +0100153
Michal Vasko1a38c862016-01-15 15:50:07 +0100154API void
155nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
156{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200157 if (!session) {
158 ERRARG("session");
159 return;
160 } else if (!reason) {
161 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100162 return;
163 }
164
Michal Vasko142cfea2017-08-07 10:12:11 +0200165 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
166 session->killed_by = 0;
167 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100168 session->term_reason = reason;
169}
170
Michal Vasko142cfea2017-08-07 10:12:11 +0200171API void
172nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
173{
174 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
175 ERRARG("session");
176 return;
177 } else if (!sid) {
178 ERRARG("sid");
179 return;
180 }
181
182 session->killed_by = sid;
183}
184
185API void
186nc_session_set_status(struct nc_session *session, NC_STATUS status)
187{
188 if (!session) {
189 ERRARG("session");
190 return;
191 } else if (!status) {
192 ERRARG("status");
193 return;
194 }
195
196 session->status = status;
197}
198
Michal Vasko086311b2016-01-08 09:53:11 +0100199int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200200nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100201{
Michal Vasko06c860d2018-07-09 16:08:52 +0200202 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100203 int is_ipv4, sock;
204 struct sockaddr_storage saddr;
205
206 struct sockaddr_in *saddr4;
207 struct sockaddr_in6 *saddr6;
208
Michal Vasko086311b2016-01-08 09:53:11 +0100209 if (!strchr(address, ':')) {
210 is_ipv4 = 1;
211 } else {
212 is_ipv4 = 0;
213 }
214
215 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
216 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200217 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100218 goto fail;
219 }
220
Michal Vaskobe52dc22018-10-17 09:28:17 +0200221 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200222 opt = 1;
223 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200224 ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko06c860d2018-07-09 16:08:52 +0200225 goto fail;
226 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100227 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200228 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vasko83ad17e2019-01-30 10:11:37 +0100229 goto fail;
230 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200231
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200232 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100233 goto fail;
234 }
235
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200236 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100237 if (is_ipv4) {
238 saddr4 = (struct sockaddr_in *)&saddr;
239
240 saddr4->sin_family = AF_INET;
241 saddr4->sin_port = htons(port);
242
243 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200244 ERR(NULL, "Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100245 goto fail;
246 }
247
248 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200249 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100250 goto fail;
251 }
252
253 } else {
254 saddr6 = (struct sockaddr_in6 *)&saddr;
255
256 saddr6->sin6_family = AF_INET6;
257 saddr6->sin6_port = htons(port);
258
259 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200260 ERR(NULL, "Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100261 goto fail;
262 }
263
264 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200265 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100266 goto fail;
267 }
268 }
269
Michal Vaskofb89d772016-01-08 12:25:35 +0100270 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200271 ERR(NULL, "Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100272 goto fail;
273 }
274
275 return sock;
276
277fail:
278 if (sock > -1) {
279 close(sock);
280 }
281
282 return -1;
283}
284
285int
roman83683fb2023-02-24 09:15:23 +0100286nc_sock_listen_unix(const struct nc_server_unix_opts *opts)
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200287{
288 struct sockaddr_un sun;
289 int sock = -1;
290
roman83683fb2023-02-24 09:15:23 +0100291 if (strlen(opts->address) > sizeof(sun.sun_path) - 1) {
292 ERR(NULL, "Socket path \"%s\" is longer than maximum length %d.", opts->address, (int)(sizeof(sun.sun_path) - 1));
Michal Vasko93e96f12021-09-30 10:02:09 +0200293 goto fail;
294 }
295
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200296 sock = socket(AF_UNIX, SOCK_STREAM, 0);
297 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200298 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200299 goto fail;
300 }
301
302 memset(&sun, 0, sizeof(sun));
303 sun.sun_family = AF_UNIX;
roman83683fb2023-02-24 09:15:23 +0100304 snprintf(sun.sun_path, sizeof(sun.sun_path) - 1, "%s", opts->address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200305
306 unlink(sun.sun_path);
307 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
roman83683fb2023-02-24 09:15:23 +0100308 ERR(NULL, "Could not bind \"%s\" (%s).", opts->address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200309 goto fail;
310 }
311
312 if (opts->mode != (mode_t)-1) {
313 if (chmod(sun.sun_path, opts->mode) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200314 ERR(NULL, "Failed to set unix socket permissions (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200315 goto fail;
316 }
317 }
318
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200319 if ((opts->uid != (uid_t)-1) || (opts->gid != (gid_t)-1)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200320 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200321 ERR(NULL, "Failed to set unix socket uid/gid (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200322 goto fail;
323 }
324 }
325
326 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
roman83683fb2023-02-24 09:15:23 +0100327 ERR(NULL, "Unable to start listening on \"%s\" (%s).", opts->address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200328 goto fail;
329 }
330
331 return sock;
332
333fail:
334 if (sock > -1) {
335 close(sock);
336 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200337 return -1;
338}
339
aPiecek90ff0242021-02-14 14:58:01 +0100340/**
341 * @brief Evaluate socket name for AF_UNIX socket.
342 * @param[in] acc_sock_fd is file descriptor for the accepted socket (a nonnegative).
343 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
344 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
345 * @return 0 if the stream socket is unnamed. Parameter host is set to NULL.
346 * @return -1 in case of error. Parameter host is set to NULL.
347 */
348static int
349sock_host_unix(int acc_sock_fd, char **host)
350{
351 char *sun_path;
352 struct sockaddr_storage saddr;
353 socklen_t addr_len;
354
355 *host = NULL;
356 saddr.ss_family = AF_UNIX;
357 addr_len = sizeof(saddr);
358
359 if (getsockname(acc_sock_fd, (struct sockaddr *)&saddr, &addr_len)) {
Michal Vasko05532772021-06-03 12:12:38 +0200360 ERR(NULL, "getsockname failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100361 return -1;
362 }
363
364 sun_path = ((struct sockaddr_un *)&saddr)->sun_path;
365 if (!sun_path) {
366 /* stream socket is unnamed */
367 return 0;
368 }
369
370 if (!(*host = strdup(sun_path))) {
371 ERRMEM;
372 return -1;
373 }
374
375 return 0;
376}
377
378/**
379 * @brief Evaluate socket name and port number for AF_INET socket.
380 * @param[in] addr is pointing to structure filled by accept function which was successful.
381 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
382 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
383 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
384 * @return -1 in case of error. Parameter host is set to NULL and port is unchanged.
385 */
386static int
387sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port)
388{
389 *host = malloc(INET_ADDRSTRLEN);
390 if (!(*host)) {
391 ERRMEM;
392 return -1;
393 }
394
aPiecek3da9b342021-02-18 15:00:03 +0100395 if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko69e98752022-12-14 14:20:17 +0100396 ERR(NULL, "inet_ntop failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100397 free(*host);
398 *host = NULL;
399 return -1;
400 }
401
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200402 *port = ntohs(addr->sin_port);
aPiecek90ff0242021-02-14 14:58:01 +0100403
404 return 0;
405}
406
407/**
408 * @brief Evaluate socket name and port number for AF_INET6 socket.
409 * @param[in] addr is pointing to structure filled by accept function which was successful.
410 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
411 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
412 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
413 * @return -1 in case of error. Parameter host is set to the NULL and port is unchanged.
414 */
415static int
416sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port)
417{
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200418 *host = malloc(INET6_ADDRSTRLEN);
aPiecek90ff0242021-02-14 14:58:01 +0100419 if (!(*host)) {
420 ERRMEM;
421 return -1;
422 }
423
aPiecek3da9b342021-02-18 15:00:03 +0100424 if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko69e98752022-12-14 14:20:17 +0100425 ERR(NULL, "inet_ntop failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100426 free(*host);
427 *host = NULL;
428 return -1;
429 }
430
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200431 *port = ntohs(addr->sin6_port);
aPiecek90ff0242021-02-14 14:58:01 +0100432
433 return 0;
434}
435
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200436int
Michal Vasko3031aae2016-01-27 16:07:18 +0100437nc_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 +0100438{
Michal Vaskof54cd352017-02-22 13:42:02 +0100439 sigset_t sigmask, origmask;
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200440 uint16_t i, j, pfd_count, client_port;
441 char *client_address;
Michal Vasko086311b2016-01-08 09:53:11 +0100442 struct pollfd *pfd;
443 struct sockaddr_storage saddr;
444 socklen_t saddr_len = sizeof(saddr);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200445 int ret, client_sock, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100446
447 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100448 if (!pfd) {
449 ERRMEM;
450 return -1;
451 }
452
Michal Vaskoac2f6182017-01-30 14:32:03 +0100453 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200454 if (binds[i].sock < 0) {
455 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200456 continue;
457 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200458 if (binds[i].pollin) {
459 binds[i].pollin = 0;
460 /* leftover pollin */
461 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100462 break;
463 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100464 pfd[pfd_count].fd = binds[i].sock;
465 pfd[pfd_count].events = POLLIN;
466 pfd[pfd_count].revents = 0;
467
468 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100469 }
470
Michal Vasko0a3f3752016-10-13 14:58:38 +0200471 if (sock == -1) {
472 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100473 sigfillset(&sigmask);
474 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100475 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100476 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
477
Michal Vasko0a3f3752016-10-13 14:58:38 +0200478 if (!ret) {
479 /* we timeouted */
480 free(pfd);
481 return 0;
482 } else if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200483 ERR(NULL, "Poll failed (%s).", strerror(errno));
Michal Vasko0a3f3752016-10-13 14:58:38 +0200484 free(pfd);
485 return -1;
486 }
Michal Vasko086311b2016-01-08 09:53:11 +0100487
Michal Vaskoac2f6182017-01-30 14:32:03 +0100488 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
489 /* adjust i so that indices in binds and pfd always match */
490 while (binds[i].sock != pfd[j].fd) {
491 ++i;
492 }
493
494 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200495 --ret;
496
497 if (!ret) {
498 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100499 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200500 break;
501 } else {
502 /* just remember the event for next time */
503 binds[i].pollin = 1;
504 }
505 }
Michal Vasko086311b2016-01-08 09:53:11 +0100506 }
507 }
508 free(pfd);
509
510 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100511 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100512 return -1;
513 }
514
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200515 /* accept connection */
516 client_sock = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
517 if (client_sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200518 ERR(NULL, "Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100519 return -1;
520 }
521
Michal Vasko0190bc32016-03-02 15:47:49 +0100522 /* make the socket non-blocking */
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200523 if (((flags = fcntl(client_sock, F_GETFL)) == -1) || (fcntl(client_sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200524 ERR(NULL, "Fcntl failed (%s).", strerror(errno));
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200525 goto fail;
Michal Vasko0190bc32016-03-02 15:47:49 +0100526 }
527
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200528 /* learn information about the client end */
529 if (saddr.ss_family == AF_UNIX) {
530 if (sock_host_unix(client_sock, &client_address)) {
531 goto fail;
532 }
533 client_port = 0;
534 } else if (saddr.ss_family == AF_INET) {
535 if (sock_host_inet((struct sockaddr_in *)&saddr, &client_address, &client_port)) {
536 goto fail;
537 }
538 } else if (saddr.ss_family == AF_INET6) {
539 if (sock_host_inet6((struct sockaddr_in6 *)&saddr, &client_address, &client_port)) {
540 goto fail;
541 }
542 } else {
543 ERR(NULL, "Source host of an unknown protocol family.");
544 goto fail;
aPiecek90ff0242021-02-14 14:58:01 +0100545 }
Michal Vasko086311b2016-01-08 09:53:11 +0100546
aPiecek90ff0242021-02-14 14:58:01 +0100547 if (saddr.ss_family == AF_UNIX) {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200548 VRB(NULL, "Accepted a connection on %s.", binds[i].address);
aPiecek90ff0242021-02-14 14:58:01 +0100549 } else {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200550 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 +0100551 }
552
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200553 if (host) {
554 *host = client_address;
555 } else {
556 free(client_address);
557 }
558 if (port) {
559 *port = client_port;
560 }
561 if (idx) {
562 *idx = i;
563 }
564 return client_sock;
565
566fail:
567 close(client_sock);
568 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100569}
570
Michal Vasko238b6c12021-12-14 15:14:09 +0100571API struct nc_server_reply *
Michal Vasko05532772021-06-03 12:12:38 +0200572nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100573{
Michal Vasko77367452021-02-16 16:32:18 +0100574 const char *identifier = NULL, *revision = NULL, *format = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100575 char *model_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100576 struct ly_out *out;
Michal Vasko9b1a9522021-03-15 16:24:26 +0100577 const struct lys_module *module = NULL, *mod;
Michal Vasko77367452021-02-16 16:32:18 +0100578 const struct lysp_submodule *submodule = NULL;
579 struct lyd_node *child, *err, *data = NULL;
580 LYS_OUTFORMAT outformat = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100581
Michal Vasko77367452021-02-16 16:32:18 +0100582 LY_LIST_FOR(lyd_child(rpc), child) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100583 if (!strcmp(child->schema->name, "identifier")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200584 identifier = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100585 } else if (!strcmp(child->schema->name, "version")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200586 revision = lyd_get_value(child);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200587 if (revision && (revision[0] == '\0')) {
Michal Vasko77367452021-02-16 16:32:18 +0100588 revision = NULL;
Radek Krejci1afa7792017-03-26 11:24:16 -0500589 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100590 } else if (!strcmp(child->schema->name, "format")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200591 format = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100592 }
593 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200594 VRB(session, "Module \"%s@%s\" was requested.", identifier, revision ? revision : "<any>");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100595
Michal Vasko77367452021-02-16 16:32:18 +0100596 /* check revision */
597 if (revision && (strlen(revision) != 10) && strcmp(revision, "1.0")) {
Michal Vasko93224072021-11-09 12:14:28 +0100598 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100599 nc_err_set_msg(err, "The requested version is not supported.", "en");
600 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100601 }
602
Michal Vasko77367452021-02-16 16:32:18 +0100603 if (revision) {
604 /* get specific module */
Michal Vasko93224072021-11-09 12:14:28 +0100605 module = ly_ctx_get_module(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100606 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100607 submodule = ly_ctx_get_submodule(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100608 }
609 } else {
610 /* try to get implemented, then latest module */
Michal Vasko93224072021-11-09 12:14:28 +0100611 module = ly_ctx_get_module_implemented(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100612 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100613 module = ly_ctx_get_module_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100614 }
615 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100616 submodule = ly_ctx_get_submodule_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100617 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200618 }
Michal Vasko77367452021-02-16 16:32:18 +0100619 if (!module && !submodule) {
Michal Vasko93224072021-11-09 12:14:28 +0100620 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko5ca5d972022-09-14 13:51:31 +0200621 nc_err_set_msg(err, "The requested module was not found.", "en");
Michal Vasko1a38c862016-01-15 15:50:07 +0100622 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100623 }
624
625 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100626 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko77367452021-02-16 16:32:18 +0100627 outformat = LYS_OUT_YANG;
Radek Krejci90fba642016-12-07 15:59:45 +0100628 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko77367452021-02-16 16:32:18 +0100629 outformat = LYS_OUT_YIN;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100630 } else {
Michal Vasko93224072021-11-09 12:14:28 +0100631 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100632 nc_err_set_msg(err, "The requested format is not supported.", "en");
633 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100634 }
Michal Vasko77367452021-02-16 16:32:18 +0100635
636 /* print */
637 ly_out_new_memory(&model_data, 0, &out);
638 if (module) {
639 lys_print_module(out, module, outformat, 0, 0);
640 } else {
641 lys_print_submodule(out, submodule, outformat, 0, 0);
642 }
643 ly_out_free(out, NULL, 0);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200644 if (!model_data) {
645 ERRINT;
646 return NULL;
647 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100648
Michal Vasko9b1a9522021-03-15 16:24:26 +0100649 /* create reply */
Michal Vasko93224072021-11-09 12:14:28 +0100650 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vasko9b1a9522021-03-15 16:24:26 +0100651 if (!mod || lyd_new_inner(NULL, mod, "get-schema", 0, &data)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100652 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200653 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100654 return NULL;
655 }
Michal Vasko9b1a9522021-03-15 16:24:26 +0100656 if (lyd_new_any(data, NULL, "data", model_data, 1, LYD_ANYDATA_STRING, 1, NULL)) {
657 ERRINT;
Michal Vaskoa50f68e2022-02-24 16:10:54 +0100658 free(model_data);
Michal Vasko9b1a9522021-03-15 16:24:26 +0100659 lyd_free_tree(data);
660 return NULL;
661 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100662
Radek Krejci36dfdb32016-09-01 16:56:35 +0200663 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100664}
665
Michal Vasko238b6c12021-12-14 15:14:09 +0100666API struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100667nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100668{
Michal Vasko428087d2016-01-14 16:04:28 +0100669 session->term_reason = NC_SESSION_TERM_CLOSED;
670 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100671}
672
Michal Vasko93224072021-11-09 12:14:28 +0100673/**
674 * @brief Initialize a context with default RPC callbacks if none are set.
675 *
676 * @param[in] ctx Context to initialize.
677 */
678static void
679nc_server_init_ctx(const struct ly_ctx *ctx)
Michal Vasko086311b2016-01-08 09:53:11 +0100680{
Michal Vasko77367452021-02-16 16:32:18 +0100681 struct lysc_node *rpc;
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100682
Michal Vasko238b6c12021-12-14 15:14:09 +0100683 if (global_rpc_clb) {
684 /* expect it to handle these RPCs as well */
685 return;
686 }
687
Michal Vasko05ba9df2016-01-13 14:40:27 +0100688 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100689 rpc = NULL;
690 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
691 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
692 }
Michal Vasko88639e92017-08-03 14:38:10 +0200693 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100694 rpc->priv = nc_clb_default_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100695 }
696
Michal Vasko93224072021-11-09 12:14:28 +0100697 /* set default <close-session> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100698 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200699 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100700 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100701 }
Michal Vasko93224072021-11-09 12:14:28 +0100702}
Michal Vasko05ba9df2016-01-13 14:40:27 +0100703
Michal Vasko93224072021-11-09 12:14:28 +0100704API int
705nc_server_init(void)
706{
707 pthread_rwlockattr_t attr, *attr_p = NULL;
708 int r;
709
710 nc_init();
Michal Vaskob48aa812016-01-18 14:13:09 +0100711
712 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500713 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100714
Michal Vasko93224072021-11-09 12:14:28 +0100715#ifdef HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
716 if ((r = pthread_rwlockattr_init(&attr))) {
717 ERR(NULL, "%s: failed init attribute (%s).", __func__, strerror(r));
718 goto error;
719 }
720 attr_p = &attr;
721 if ((r = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))) {
722 ERR(NULL, "%s: failed set attribute (%s).", __func__, strerror(r));
723 goto error;
724 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700725#endif
Michal Vasko93224072021-11-09 12:14:28 +0100726
727 if ((r = pthread_rwlock_init(&server_opts.endpt_lock, attr_p))) {
728 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
729 goto error;
730 }
731 if ((r = pthread_rwlock_init(&server_opts.ch_client_lock, attr_p))) {
732 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
733 goto error;
734 }
735
736 if (attr_p) {
737 pthread_rwlockattr_destroy(attr_p);
Frank Rimpler9f838b02018-07-25 06:44:03 +0000738 }
Michal Vasko086311b2016-01-08 09:53:11 +0100739 return 0;
Michal Vasko93224072021-11-09 12:14:28 +0100740
741error:
742 if (attr_p) {
743 pthread_rwlockattr_destroy(attr_p);
744 }
745 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100746}
747
Michal Vaskob48aa812016-01-18 14:13:09 +0100748API void
749nc_server_destroy(void)
750{
Michal Vasko1440a742021-03-31 11:11:03 +0200751 uint32_t i;
Radek Krejci658782b2016-12-04 22:04:55 +0100752
753 for (i = 0; i < server_opts.capabilities_count; i++) {
Michal Vasko93224072021-11-09 12:14:28 +0100754 free(server_opts.capabilities[i]);
Radek Krejci658782b2016-12-04 22:04:55 +0100755 }
756 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200757 server_opts.capabilities = NULL;
758 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200759 if (server_opts.content_id_data && server_opts.content_id_data_free) {
760 server_opts.content_id_data_free(server_opts.content_id_data);
761 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200762
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200763#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100764 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200765 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100766#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100767#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100768 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
769 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
770 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200771 server_opts.passwd_auth_data = NULL;
772 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100773
Michal Vasko1c2d2652023-10-17 08:53:36 +0200774 if (server_opts.pubkey_auth_data && server_opts.pubkey_auth_data_free) {
775 server_opts.pubkey_auth_data_free(server_opts.pubkey_auth_data);
776 }
777 server_opts.pubkey_auth_data = NULL;
778 server_opts.pubkey_auth_data_free = NULL;
779
780 if (server_opts.interactive_auth_sess_data && server_opts.interactive_auth_sess_data_free) {
781 server_opts.interactive_auth_sess_data_free(server_opts.interactive_auth_sess_data);
782 }
783 server_opts.interactive_auth_sess_data = NULL;
784 server_opts.interactive_auth_sess_data_free = NULL;
785
786 if (server_opts.interactive_auth_data && server_opts.interactive_auth_data_free) {
787 server_opts.interactive_auth_data_free(server_opts.interactive_auth_data);
788 }
789 server_opts.interactive_auth_data = NULL;
790 server_opts.interactive_auth_data_free = NULL;
791
Michal Vasko17dfda92016-12-01 14:06:16 +0100792 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100793
794 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
795 server_opts.hostkey_data_free(server_opts.hostkey_data);
796 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200797 server_opts.hostkey_data = NULL;
798 server_opts.hostkey_data_free = NULL;
roman41a11e42022-06-22 09:27:08 +0200799
800 /* PAM */
801 free(server_opts.conf_name);
802 free(server_opts.conf_dir);
803 server_opts.conf_name = NULL;
804 server_opts.conf_dir = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100805#endif
806#ifdef NC_ENABLED_TLS
807 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
808 server_opts.server_cert_data_free(server_opts.server_cert_data);
809 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200810 server_opts.server_cert_data = NULL;
811 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100812 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
813 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
814 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200815 server_opts.trusted_cert_list_data = NULL;
816 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100817#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100818 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100819}
820
Michal Vasko086311b2016-01-08 09:53:11 +0100821API int
822nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
823{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200824 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
825 ERRARG("basic_mode");
826 return -1;
827 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
828 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100829 return -1;
830 }
831
832 server_opts.wd_basic_mode = basic_mode;
833 server_opts.wd_also_supported = also_supported;
834 return 0;
835}
836
Michal Vasko1a38c862016-01-15 15:50:07 +0100837API void
Michal Vasko55f03972016-04-13 08:56:01 +0200838nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
839{
840 if (!basic_mode && !also_supported) {
841 ERRARG("basic_mode and also_supported");
842 return;
843 }
844
845 if (basic_mode) {
846 *basic_mode = server_opts.wd_basic_mode;
847 }
848 if (also_supported) {
849 *also_supported = server_opts.wd_also_supported;
850 }
851}
852
Michal Vasko55f03972016-04-13 08:56:01 +0200853API int
Radek Krejci658782b2016-12-04 22:04:55 +0100854nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200855{
Michal Vasko93224072021-11-09 12:14:28 +0100856 void *mem;
Radek Krejci658782b2016-12-04 22:04:55 +0100857
858 if (!value || !value[0]) {
859 ERRARG("value must not be empty");
860 return EXIT_FAILURE;
861 }
862
Michal Vasko93224072021-11-09 12:14:28 +0100863 mem = realloc(server_opts.capabilities, (server_opts.capabilities_count + 1) * sizeof *server_opts.capabilities);
864 if (!mem) {
Radek Krejci658782b2016-12-04 22:04:55 +0100865 ERRMEM;
866 return EXIT_FAILURE;
867 }
Michal Vasko93224072021-11-09 12:14:28 +0100868 server_opts.capabilities = mem;
869
870 server_opts.capabilities[server_opts.capabilities_count] = strdup(value);
871 server_opts.capabilities_count++;
Radek Krejci658782b2016-12-04 22:04:55 +0100872
873 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200874}
875
Michal Vasko1a38c862016-01-15 15:50:07 +0100876API void
Michal Vasko1440a742021-03-31 11:11:03 +0200877nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
878 void (*free_user_data)(void *user_data))
879{
880 server_opts.content_id_clb = content_id_clb;
881 server_opts.content_id_data = user_data;
882 server_opts.content_id_data_free = free_user_data;
883}
884
885API void
Michal Vasko086311b2016-01-08 09:53:11 +0100886nc_server_set_hello_timeout(uint16_t hello_timeout)
887{
Michal Vasko086311b2016-01-08 09:53:11 +0100888 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100889}
890
Michal Vasko55f03972016-04-13 08:56:01 +0200891API uint16_t
892nc_server_get_hello_timeout(void)
893{
894 return server_opts.hello_timeout;
895}
896
Michal Vasko1a38c862016-01-15 15:50:07 +0100897API void
Michal Vasko086311b2016-01-08 09:53:11 +0100898nc_server_set_idle_timeout(uint16_t idle_timeout)
899{
Michal Vasko086311b2016-01-08 09:53:11 +0100900 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100901}
902
Michal Vasko55f03972016-04-13 08:56:01 +0200903API uint16_t
904nc_server_get_idle_timeout(void)
905{
906 return server_opts.idle_timeout;
907}
908
Michal Vasko71090fc2016-05-24 16:37:28 +0200909API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +0100910nc_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 +0100911{
Michal Vasko71090fc2016-05-24 16:37:28 +0200912 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200913 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200914
Michal Vasko93224072021-11-09 12:14:28 +0100915 if (!ctx) {
916 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +0200917 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200918 } else if (fdin < 0) {
919 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200920 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200921 } else if (fdout < 0) {
922 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200923 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200924 } else if (!username) {
925 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200926 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200927 } else if (!session) {
928 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200929 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100930 }
931
Michal Vasko93224072021-11-09 12:14:28 +0100932 /* init ctx as needed */
933 nc_server_init_ctx(ctx);
934
Michal Vasko086311b2016-01-08 09:53:11 +0100935 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200936 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100937 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100938 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200939 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100940 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100941 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100942
Michal Vasko086311b2016-01-08 09:53:11 +0100943 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100944 (*session)->ti_type = NC_TI_FD;
945 (*session)->ti.fd.in = fdin;
946 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100947
Michal Vasko93224072021-11-09 12:14:28 +0100948 /* assign context */
Michal Vasko1a38c862016-01-15 15:50:07 +0100949 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +0100950 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100951
Michal Vaskob48aa812016-01-18 14:13:09 +0100952 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200953 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100954
Michal Vasko086311b2016-01-08 09:53:11 +0100955 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200956 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200957 if (msgtype != NC_MSG_HELLO) {
958 nc_session_free(*session, NULL);
959 *session = NULL;
960 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100961 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200962
Michal Vaskod8a74192023-02-06 15:51:50 +0100963 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +0200964 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +0100965 nc_realtime_get(&ts_cur);
Michal Vasko9fb42272017-10-05 13:50:05 +0200966 (*session)->opts.server.session_start = ts_cur.tv_sec;
967
Michal Vasko1a38c862016-01-15 15:50:07 +0100968 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100969
Michal Vasko71090fc2016-05-24 16:37:28 +0200970 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100971}
Michal Vasko9e036d52016-01-08 10:49:26 +0100972
Michal Vaskob30b99c2016-07-26 11:35:43 +0200973static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100974nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
975{
976 uint8_t q_last;
977
978 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
979 ERRINT;
980 return;
981 }
982
983 /* get a unique queue value (by adding 1 to the last added value, if any) */
984 if (ps->queue_len) {
985 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
986 *id = ps->queue[q_last] + 1;
987 } else {
988 *id = 0;
989 }
990
991 /* add the id into the queue */
992 ++ps->queue_len;
993 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
994 ps->queue[q_last] = *id;
995}
996
997static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200998nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
999{
Michal Vasko74c345f2018-02-07 10:37:11 +01001000 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001001
1002 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +01001003 /* get the actual queue idx */
1004 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001005
1006 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +01001007 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +02001008 /* another equal value, simply cannot be */
1009 ERRINT;
1010 }
Michal Vaskod8340032018-02-12 14:41:00 +01001011 if (found == 2) {
1012 /* move the following values */
1013 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
1014 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001015 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +02001016 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +01001017 if (i == 0) {
1018 found = 1;
1019 } else {
1020 /* this is not okay, our id is in the middle of the queue */
1021 found = 2;
1022 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001023 }
1024 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001025 if (!found) {
1026 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +01001027 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001028 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001029
Michal Vasko103fe632018-02-12 16:37:45 +01001030 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +01001031 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +01001032 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +01001033 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
1034 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001035}
1036
Michal Vaskof04a52a2016-04-07 10:52:10 +02001037int
Michal Vasko26043172016-07-26 14:08:59 +02001038nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001039{
1040 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001041 struct timespec ts;
1042
Michal Vaskobe86fe32016-04-07 10:43:03 +02001043 /* LOCK */
Michal Vasko8c7def52023-06-06 14:48:56 +02001044 ret = pthread_mutex_lock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001045 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001046 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001047 return -1;
1048 }
1049
Michal Vasko74c345f2018-02-07 10:37:11 +01001050 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +01001051 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko05532772021-06-03 12:12:38 +02001052 ERR(NULL, "%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +01001053 pthread_mutex_unlock(&ps->lock);
1054 return -1;
1055 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001056
1057 /* add ourselves into the queue */
1058 nc_ps_queue_add_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001059 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 +02001060 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001061
1062 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001063 while (ps->queue[ps->queue_begin] != *id) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001064 nc_timeouttime_get(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001065
Michal Vaskod8a74192023-02-06 15:51:50 +01001066 ret = pthread_cond_clockwait(&ps->cond, &ps->lock, COMPAT_CLOCK_ID, &ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001067 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301068 /**
1069 * This may happen when another thread releases the lock and broadcasts the condition
1070 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1071 * but when actually this thread was ready for condition.
1072 */
preetbhansali629dfc42018-12-17 16:04:40 +05301073 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301074 break;
1075 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001076
Michal Vasko05532772021-06-03 12:12:38 +02001077 ERR(NULL, "%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001078 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001079 nc_ps_queue_remove_id(ps, *id);
1080 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001081 return -1;
1082 }
1083 }
1084
Michal Vaskobe86fe32016-04-07 10:43:03 +02001085 /* UNLOCK */
1086 pthread_mutex_unlock(&ps->lock);
1087
1088 return 0;
1089}
1090
Michal Vaskof04a52a2016-04-07 10:52:10 +02001091int
Michal Vasko26043172016-07-26 14:08:59 +02001092nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001093{
1094 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001095
1096 /* LOCK */
Michal Vasko8c7def52023-06-06 14:48:56 +02001097 ret = pthread_mutex_lock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001098 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001099 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001100 ret = -1;
1101 }
1102
Michal Vaskob30b99c2016-07-26 11:35:43 +02001103 /* we must be the first, it was our turn after all, right? */
1104 if (ps->queue[ps->queue_begin] != id) {
1105 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001106 /* UNLOCK */
1107 if (!ret) {
1108 pthread_mutex_unlock(&ps->lock);
1109 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001110 return -1;
1111 }
1112
Michal Vaskobe86fe32016-04-07 10:43:03 +02001113 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001114 nc_ps_queue_remove_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001115 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 +02001116 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001117
1118 /* broadcast to all other threads that the queue moved */
1119 pthread_cond_broadcast(&ps->cond);
1120
Michal Vaskobe86fe32016-04-07 10:43:03 +02001121 /* UNLOCK */
1122 if (!ret) {
1123 pthread_mutex_unlock(&ps->lock);
1124 }
1125
1126 return ret;
1127}
1128
Michal Vasko428087d2016-01-14 16:04:28 +01001129API struct nc_pollsession *
1130nc_ps_new(void)
1131{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001132 struct nc_pollsession *ps;
1133
1134 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001135 if (!ps) {
1136 ERRMEM;
1137 return NULL;
1138 }
Michal Vaskobe86fe32016-04-07 10:43:03 +02001139 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001140 pthread_mutex_init(&ps->lock, NULL);
1141
1142 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001143}
1144
1145API void
1146nc_ps_free(struct nc_pollsession *ps)
1147{
fanchanghu3d4e7212017-08-09 09:42:30 +08001148 uint16_t i;
1149
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001150 if (!ps) {
1151 return;
1152 }
1153
Michal Vaskobe86fe32016-04-07 10:43:03 +02001154 if (ps->queue_len) {
Michal Vasko05532772021-06-03 12:12:38 +02001155 ERR(NULL, "FATAL: Freeing a pollsession structure that is currently being worked with!");
Michal Vaskobe86fe32016-04-07 10:43:03 +02001156 }
1157
fanchanghu3d4e7212017-08-09 09:42:30 +08001158 for (i = 0; i < ps->session_count; i++) {
1159 free(ps->sessions[i]);
1160 }
1161
Michal Vasko428087d2016-01-14 16:04:28 +01001162 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001163 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001164 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001165
Michal Vasko428087d2016-01-14 16:04:28 +01001166 free(ps);
1167}
1168
1169API int
1170nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1171{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001172 uint8_t q_id;
1173
Michal Vasko45e53ae2016-04-07 11:46:03 +02001174 if (!ps) {
1175 ERRARG("ps");
1176 return -1;
1177 } else if (!session) {
1178 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001179 return -1;
1180 }
1181
Michal Vasko48a63ed2016-03-01 09:48:21 +01001182 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001183 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001184 return -1;
1185 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001186
Michal Vasko428087d2016-01-14 16:04:28 +01001187 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001188 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001189 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001190 ERRMEM;
1191 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001192 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001193 return -1;
1194 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001195 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1196 if (!ps->sessions[ps->session_count - 1]) {
1197 ERRMEM;
1198 --ps->session_count;
1199 /* UNLOCK */
1200 nc_ps_unlock(ps, q_id, __func__);
1201 return -1;
1202 }
1203 ps->sessions[ps->session_count - 1]->session = session;
1204 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001205
Michal Vasko48a63ed2016-03-01 09:48:21 +01001206 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001207 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001208}
1209
Michal Vasko48a63ed2016-03-01 09:48:21 +01001210static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001211_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001212{
1213 uint16_t i;
1214
Radek Krejcid5f978f2016-03-03 13:14:45 +01001215 if (index >= 0) {
1216 i = (uint16_t)index;
1217 goto remove;
1218 }
Michal Vasko428087d2016-01-14 16:04:28 +01001219 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001220 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001221remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001222 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001223 if (i <= ps->session_count) {
1224 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001225 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001226 }
1227 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001228 free(ps->sessions);
1229 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001230 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001231 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001232 return 0;
1233 }
1234 }
1235
Michal Vaskof0537d82016-01-29 14:42:38 +01001236 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001237}
1238
Michal Vasko48a63ed2016-03-01 09:48:21 +01001239API int
1240nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1241{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001242 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001243 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001244
Michal Vasko45e53ae2016-04-07 11:46:03 +02001245 if (!ps) {
1246 ERRARG("ps");
1247 return -1;
1248 } else if (!session) {
1249 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001250 return -1;
1251 }
1252
1253 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001254 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001255 return -1;
1256 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001257
Radek Krejcid5f978f2016-03-03 13:14:45 +01001258 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001259
1260 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001261 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001262
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001263 return ret || ret2 ? -1 : 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001264}
1265
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001266API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001267nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001268{
1269 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001270 struct nc_session *ret = NULL;
1271
1272 if (!ps) {
1273 ERRARG("ps");
1274 return NULL;
1275 }
1276
1277 /* LOCK */
1278 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1279 return NULL;
1280 }
1281
Michal Vasko4871c9d2017-10-09 14:48:39 +02001282 if (idx < ps->session_count) {
1283 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001284 }
1285
1286 /* UNLOCK */
1287 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1288
1289 return ret;
1290}
1291
Michal Vasko3ec3b112022-07-21 12:32:33 +02001292API struct nc_session *
1293nc_ps_find_session(const struct nc_pollsession *ps, nc_ps_session_match_cb match_cb, void *cb_data)
1294{
1295 uint8_t q_id;
1296 uint16_t i;
1297 struct nc_session *ret = NULL;
1298
1299 if (!ps) {
1300 ERRARG("ps");
1301 return NULL;
1302 }
1303
1304 /* LOCK */
1305 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1306 return NULL;
1307 }
1308
1309 for (i = 0; i < ps->session_count; ++i) {
1310 if (match_cb(ps->sessions[i]->session, cb_data)) {
1311 ret = ps->sessions[i]->session;
1312 break;
1313 }
1314 }
1315
1316 /* UNLOCK */
1317 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1318
1319 return ret;
1320}
1321
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001322API uint16_t
1323nc_ps_session_count(struct nc_pollsession *ps)
1324{
Michal Vasko47003942019-03-14 12:25:23 +01001325 uint8_t q_id;
1326 uint16_t session_count;
1327
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001328 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001329 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001330 return 0;
1331 }
1332
Michal Vasko47003942019-03-14 12:25:23 +01001333 /* LOCK (just for memory barrier so that we read the current value) */
1334 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1335 return 0;
1336 }
1337
1338 session_count = ps->session_count;
1339
1340 /* UNLOCK */
1341 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1342
1343 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001344}
1345
Michal Vasko77e83572022-07-21 15:31:15 +02001346static NC_MSG_TYPE
1347recv_rpc_check_msgid(struct nc_session *session, const struct lyd_node *envp)
1348{
1349 struct lyd_attr *attr;
1350
1351 assert(envp && !envp->schema);
1352
1353 /* find the message-id attribute */
1354 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1355 if (!strcmp(attr->name.name, "message-id")) {
1356 break;
1357 }
1358 }
1359
1360 if (!attr) {
1361 ERR(session, "Received an <rpc> without a message-id.");
1362 return NC_MSG_REPLY_ERR_MSGID;
1363 }
1364
1365 return NC_MSG_RPC;
1366}
1367
Michal Vasko131120a2018-05-29 15:44:02 +02001368/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001369 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001370 * NC_PSPOLL_TIMEOUT,
Michal Vaskof8fba542023-10-23 12:03:50 +02001371 * NC_PSPOLL_BAD_RPC (| NC_PSPOLL_REPLY_ERROR),
Michal Vasko71090fc2016-05-24 16:37:28 +02001372 * NC_PSPOLL_RPC
1373 */
1374static int
Michal Vasko131120a2018-05-29 15:44:02 +02001375nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001376{
Michal Vasko77367452021-02-16 16:32:18 +01001377 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001378 struct nc_server_reply *reply = NULL;
Michal Vasko939ffce2021-04-12 13:02:01 +02001379 struct lyd_node *e;
Michal Vaskof8fba542023-10-23 12:03:50 +02001380 int r, ret = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001381
Michal Vasko45e53ae2016-04-07 11:46:03 +02001382 if (!session) {
1383 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001384 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001385 } else if (!rpc) {
1386 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001387 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001388 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vasko05532772021-06-03 12:12:38 +02001389 ERR(session, "Invalid session to receive RPCs.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001390 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001391 }
1392
Michal Vasko93224072021-11-09 12:14:28 +01001393 *rpc = NULL;
1394
Michal Vasko77367452021-02-16 16:32:18 +01001395 /* get a message */
1396 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1397 if (r == -2) {
1398 /* malformed message */
Michal Vasko93224072021-11-09 12:14:28 +01001399 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77e83572022-07-21 15:31:15 +02001400 goto cleanup;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001401 }
1402 if (r == -1) {
Michal Vasko77367452021-02-16 16:32:18 +01001403 return NC_PSPOLL_ERROR;
1404 } else if (!r) {
1405 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001406 }
1407
Michal Vasko77367452021-02-16 16:32:18 +01001408 *rpc = calloc(1, sizeof **rpc);
1409 if (!*rpc) {
1410 ERRMEM;
Michal Vaskof8fba542023-10-23 12:03:50 +02001411 ret = NC_PSPOLL_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001412 goto cleanup;
1413 }
1414
1415 /* parse the RPC */
Michal Vasko77e83572022-07-21 15:31:15 +02001416 if (!lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_RPC_NETCONF, &(*rpc)->envp, &(*rpc)->rpc)) {
1417 /* check message-id */
1418 if (recv_rpc_check_msgid(session, (*rpc)->envp) == NC_MSG_RPC) {
1419 /* valid RPC */
1420 ret = NC_PSPOLL_RPC;
1421 } else {
1422 /* no message-id */
Michal Vasko77e83572022-07-21 15:31:15 +02001423 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MISSING_ATTR, NC_ERR_TYPE_RPC, "message-id", "rpc"));
1424 }
1425 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001426 /* bad RPC received */
Michal Vasko77367452021-02-16 16:32:18 +01001427 if ((*rpc)->envp) {
1428 /* at least the envelopes were parsed */
Michal Vasko93224072021-11-09 12:14:28 +01001429 e = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
1430 nc_err_set_msg(e, ly_errmsg(session->ctx), "en");
Michal Vasko939ffce2021-04-12 13:02:01 +02001431 reply = nc_server_reply_err(e);
Michal Vasko77367452021-02-16 16:32:18 +01001432 } else if (session->version == NC_VERSION_11) {
Michal Vasko93224072021-11-09 12:14:28 +01001433 /* completely malformed message, NETCONF version 1.1 defines sending error reply from
1434 * the server (RFC 6241 sec. 3) */
1435 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vaskof8fba542023-10-23 12:03:50 +02001436 } else {
1437 /* at least set the return value */
1438 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko77367452021-02-16 16:32:18 +01001439 }
Michal Vasko77367452021-02-16 16:32:18 +01001440 }
1441
1442cleanup:
Michal Vasko77e83572022-07-21 15:31:15 +02001443 if (reply) {
1444 /* send error reply */
1445 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, *rpc ? (*rpc)->envp : NULL, reply);
1446 nc_server_reply_free(reply);
1447 if (r != NC_MSG_REPLY) {
1448 ERR(session, "Failed to write reply (%s), terminating session.", nc_msgtype2str[r]);
1449 if (session->status != NC_STATUS_INVALID) {
1450 session->status = NC_STATUS_INVALID;
1451 session->term_reason = NC_SESSION_TERM_OTHER;
1452 }
1453 }
Michal Vaskof8fba542023-10-23 12:03:50 +02001454
1455 /* bad RPC and an error reply sent */
1456 ret = NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR;
Michal Vasko77e83572022-07-21 15:31:15 +02001457 }
1458
Michal Vasko77367452021-02-16 16:32:18 +01001459 ly_in_free(msg, 1);
1460 if (ret != NC_PSPOLL_RPC) {
1461 nc_server_rpc_free(*rpc);
1462 *rpc = NULL;
1463 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001464 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001465}
1466
fanchanghu966f2de2016-07-21 02:28:57 -04001467API void
1468nc_set_global_rpc_clb(nc_rpc_clb clb)
1469{
1470 global_rpc_clb = clb;
1471}
1472
Radek Krejci93e80222016-10-03 13:34:25 +02001473API NC_MSG_TYPE
1474nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1475{
Michal Vasko131120a2018-05-29 15:44:02 +02001476 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001477
1478 /* check parameters */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001479 if (!session || (session->side != NC_SERVER) || !nc_session_get_notif_status(session)) {
Radek Krejci93e80222016-10-03 13:34:25 +02001480 ERRARG("session");
1481 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001482 } else if (!notif || !notif->ntf || !notif->eventtime) {
Radek Krejci93e80222016-10-03 13:34:25 +02001483 ERRARG("notif");
1484 return NC_MSG_ERROR;
1485 }
1486
Michal Vasko131120a2018-05-29 15:44:02 +02001487 /* we do not need RPC lock for this, IO lock will be acquired properly */
1488 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001489 if (ret != NC_MSG_NOTIF) {
Michal Vasko05532772021-06-03 12:12:38 +02001490 ERR(session, "Failed to write notification (%s).", nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001491 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001492
Michal Vasko131120a2018-05-29 15:44:02 +02001493 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001494}
1495
Michal Vaskof9467762023-03-28 09:02:08 +02001496/**
1497 * @brief Send a reply acquiring IO lock as needed.
1498 * Session RPC lock must be held!
1499 *
1500 * @param[in] session Session to use.
1501 * @param[in] io_timeout Timeout to use for acquiring IO lock.
1502 * @param[in] rpc RPC to sent.
1503 * @return 0 on success.
1504 * @return Bitmask of NC_PSPOLL_ERROR (any fatal error) and NC_PSPOLL_REPLY_ERROR (reply failed to be sent).
1505 * @return NC_PSPOLL_ERROR on other errors.
Michal Vasko71090fc2016-05-24 16:37:28 +02001506 */
1507static int
Michal Vasko93224072021-11-09 12:14:28 +01001508nc_server_send_reply_io(struct nc_session *session, int io_timeout, const struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001509{
1510 nc_rpc_clb clb;
1511 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001512 const struct lysc_node *rpc_act = NULL;
1513 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001514 int ret = 0;
1515 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001516
Michal Vasko4a827e52016-03-03 10:59:00 +01001517 if (!rpc) {
1518 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001519 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001520 }
1521
Michal Vasko77367452021-02-16 16:32:18 +01001522 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001523 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001524 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001525 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001526 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001527 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001528 if (elem->schema->nodetype == LYS_ACTION) {
1529 rpc_act = elem->schema;
1530 break;
1531 }
Michal Vasko77367452021-02-16 16:32:18 +01001532 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001533 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001534 if (!rpc_act) {
1535 ERRINT;
1536 return NC_PSPOLL_ERROR;
1537 }
1538 }
1539
1540 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001541 if (!global_rpc_clb) {
1542 /* no callback, reply with a not-implemented error */
Michal Vasko93224072021-11-09 12:14:28 +01001543 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 +03001544 } else {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001545 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001546 }
Michal Vasko428087d2016-01-14 16:04:28 +01001547 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001548 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001549 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001550 }
1551
1552 if (!reply) {
Michal Vasko93224072021-11-09 12:14:28 +01001553 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001554 }
Michal Vasko77367452021-02-16 16:32:18 +01001555 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001556 if (reply->type == NC_RPL_ERROR) {
1557 ret |= NC_PSPOLL_REPLY_ERROR;
1558 }
1559 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001560
Michal Vasko131120a2018-05-29 15:44:02 +02001561 if (r != NC_MSG_REPLY) {
Michal Vasko15469492021-06-09 08:40:48 +02001562 ERR(session, "Failed to write reply (%s).", nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001563 ret |= NC_PSPOLL_ERROR;
1564 }
Michal Vasko428087d2016-01-14 16:04:28 +01001565
1566 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1567 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1568 session->status = NC_STATUS_INVALID;
1569 }
1570
Michal Vasko71090fc2016-05-24 16:37:28 +02001571 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001572}
1573
Michal Vaskof9467762023-03-28 09:02:08 +02001574/**
1575 * @brief Poll a session from pspoll acquiring IO lock as needed.
1576 * Session must be running and session RPC lock held!
1577 *
1578 * @param[in] session Session to use.
1579 * @param[in] io_timeout Timeout to use for acquiring IO lock.
1580 * @param[in] now_mono Current monotonic timestamp.
1581 * @param[in,out] msg Message to fill in case of an error.
1582 * @return NC_PSPOLL_RPC if some application data are available.
1583 * @return NC_PSPOLL_TIMEOUT if a timeout elapsed.
1584 * @return NC_PSPOLL_SSH_CHANNEL if a new SSH channel has been created.
1585 * @return NC_PSPOLL_SSH_MSG if just an SSH message has been processed.
1586 * @return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR if session has been terminated (@p msg filled).
1587 * @return NC_PSPOLL_ERROR on other fatal errors (@p msg filled).
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001588 */
1589static int
Michal Vasko131120a2018-05-29 15:44:02 +02001590nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001591{
Michal Vasko9a327362017-01-11 11:31:46 +01001592 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001593 int r, ret = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001594
Michal Vasko9a327362017-01-11 11:31:46 +01001595#ifdef NC_ENABLED_SSH
roman456f92d2023-04-28 10:28:12 +02001596 ssh_message ssh_msg;
Michal Vasko9a327362017-01-11 11:31:46 +01001597 struct nc_session *new;
1598#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001599
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001600 /* check timeout first */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001601 if (!(session->flags & NC_SESSION_CALLHOME) && !nc_session_get_notif_status(session) && server_opts.idle_timeout &&
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001602 (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001603 sprintf(msg, "session idle timeout elapsed");
1604 session->status = NC_STATUS_INVALID;
1605 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1606 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1607 }
1608
Michal Vasko131120a2018-05-29 15:44:02 +02001609 r = nc_session_io_lock(session, io_timeout, __func__);
1610 if (r < 0) {
1611 sprintf(msg, "session IO lock failed to be acquired");
1612 return NC_PSPOLL_ERROR;
1613 } else if (!r) {
1614 return NC_PSPOLL_TIMEOUT;
1615 }
1616
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001617 switch (session->ti_type) {
1618#ifdef NC_ENABLED_SSH
1619 case NC_TI_LIBSSH:
1620 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001621 if (r == SSH_EOF) {
1622 sprintf(msg, "SSH channel unexpected EOF");
1623 session->status = NC_STATUS_INVALID;
1624 session->term_reason = NC_SESSION_TERM_DROPPED;
1625 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1626 } else if (r == SSH_ERROR) {
1627 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001628 session->status = NC_STATUS_INVALID;
1629 session->term_reason = NC_SESSION_TERM_OTHER;
1630 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001631 } else if (!r) {
1632 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1633 /* new SSH message */
1634 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1635 if (session->ti.libssh.next) {
1636 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001637 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
1638 (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko8dcaa882017-10-19 14:28:42 +02001639 /* new NETCONF SSH channel */
1640 ret = NC_PSPOLL_SSH_CHANNEL;
1641 break;
1642 }
1643 }
1644 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001645 break;
1646 }
1647 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001648
Michal Vasko8dcaa882017-10-19 14:28:42 +02001649 /* just some SSH message */
1650 ret = NC_PSPOLL_SSH_MSG;
1651 } else {
1652 ret = NC_PSPOLL_TIMEOUT;
1653 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001654 } else {
1655 /* we have some application data */
1656 ret = NC_PSPOLL_RPC;
1657 }
1658 break;
1659#endif
1660#ifdef NC_ENABLED_TLS
1661 case NC_TI_OPENSSL:
1662 r = SSL_pending(session->ti.tls);
1663 if (!r) {
1664 /* no data pending in the SSL buffer, poll fd */
1665 pfd.fd = SSL_get_rfd(session->ti.tls);
1666 if (pfd.fd < 0) {
1667 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1668 ret = NC_PSPOLL_ERROR;
1669 break;
1670 }
1671 pfd.events = POLLIN;
1672 pfd.revents = 0;
1673 r = poll(&pfd, 1, 0);
1674
1675 if ((r < 0) && (errno != EINTR)) {
1676 sprintf(msg, "poll failed (%s)", strerror(errno));
1677 session->status = NC_STATUS_INVALID;
1678 ret = NC_PSPOLL_ERROR;
1679 } else if (r > 0) {
1680 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1681 sprintf(msg, "communication socket unexpectedly closed");
1682 session->status = NC_STATUS_INVALID;
1683 session->term_reason = NC_SESSION_TERM_DROPPED;
1684 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1685 } else if (pfd.revents & POLLERR) {
1686 sprintf(msg, "communication socket error");
1687 session->status = NC_STATUS_INVALID;
1688 session->term_reason = NC_SESSION_TERM_OTHER;
1689 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1690 } else {
1691 ret = NC_PSPOLL_RPC;
1692 }
1693 } else {
1694 ret = NC_PSPOLL_TIMEOUT;
1695 }
1696 } else {
1697 ret = NC_PSPOLL_RPC;
1698 }
1699 break;
1700#endif
1701 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001702 case NC_TI_UNIX:
1703 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001704 pfd.events = POLLIN;
1705 pfd.revents = 0;
1706 r = poll(&pfd, 1, 0);
1707
1708 if ((r < 0) && (errno != EINTR)) {
1709 sprintf(msg, "poll failed (%s)", strerror(errno));
1710 session->status = NC_STATUS_INVALID;
1711 ret = NC_PSPOLL_ERROR;
1712 } else if (r > 0) {
1713 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1714 sprintf(msg, "communication socket unexpectedly closed");
1715 session->status = NC_STATUS_INVALID;
1716 session->term_reason = NC_SESSION_TERM_DROPPED;
1717 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1718 } else if (pfd.revents & POLLERR) {
1719 sprintf(msg, "communication socket error");
1720 session->status = NC_STATUS_INVALID;
1721 session->term_reason = NC_SESSION_TERM_OTHER;
1722 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1723 } else {
1724 ret = NC_PSPOLL_RPC;
1725 }
1726 } else {
1727 ret = NC_PSPOLL_TIMEOUT;
1728 }
1729 break;
1730 case NC_TI_NONE:
1731 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1732 ret = NC_PSPOLL_ERROR;
1733 break;
1734 }
1735
Michal Vasko131120a2018-05-29 15:44:02 +02001736 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001737 return ret;
1738}
1739
1740API int
1741nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1742{
Michal Vasko443faa02022-10-20 09:09:03 +02001743 int ret = NC_PSPOLL_ERROR, r;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001744 uint8_t q_id;
1745 uint16_t i, j;
1746 char msg[256];
1747 struct timespec ts_timeout, ts_cur;
1748 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001749 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001750 struct nc_server_rpc *rpc = NULL;
1751
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001752 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001753 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001754 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001755 }
1756
Michal Vaskoade892d2017-02-22 13:40:35 +01001757 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001758 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001759 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001760 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001761
Michal Vaskoade892d2017-02-22 13:40:35 +01001762 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001763 nc_ps_unlock(ps, q_id, __func__);
1764 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001765 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001766
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001767 /* fill timespecs */
Michal Vaskod8a74192023-02-06 15:51:50 +01001768 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko36c7be82017-02-22 13:37:59 +01001769 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001770 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001771 }
1772
1773 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001774 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001775 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001776 if (ps->last_event_session == ps->session_count - 1) {
1777 i = j = 0;
1778 } else {
1779 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001780 }
Michal Vasko9a327362017-01-11 11:31:46 +01001781 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001782 cur_ps_session = ps->sessions[i];
1783 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001784
Michal Vasko131120a2018-05-29 15:44:02 +02001785 /* SESSION RPC LOCK */
1786 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001787 if (r == -1) {
1788 ret = NC_PSPOLL_ERROR;
1789 } else if (r == 1) {
1790 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001791 switch (cur_ps_session->state) {
1792 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001793 if (cur_session->status == NC_STATUS_RUNNING) {
1794 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001795 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001796
Michal Vasko131120a2018-05-29 15:44:02 +02001797 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001798 switch (ret) {
1799 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001800 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001801 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001802 break;
1803 case NC_PSPOLL_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001804 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001805 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001806 break;
1807 case NC_PSPOLL_TIMEOUT:
1808#ifdef NC_ENABLED_SSH
1809 case NC_PSPOLL_SSH_CHANNEL:
1810 case NC_PSPOLL_SSH_MSG:
1811#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001812 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001813 break;
1814 case NC_PSPOLL_RPC:
1815 /* let's keep the state busy, we are not done with this session */
1816 break;
1817 }
1818 } else {
1819 /* session is not fine, let the caller know */
1820 ret = NC_PSPOLL_SESSION_TERM;
1821 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1822 ret |= NC_PSPOLL_SESSION_ERROR;
1823 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001824 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001825 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001826 break;
1827 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001828 /* it definitely should not be busy because we have the lock */
1829 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001830 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001831 break;
1832 case NC_PS_STATE_INVALID:
1833 /* we got it locked, but it will be freed, let it be */
1834 ret = NC_PSPOLL_TIMEOUT;
1835 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001836 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001837
Michal Vasko131120a2018-05-29 15:44:02 +02001838 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001839 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001840 /* SESSION RPC UNLOCK */
1841 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001842 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001843 } else {
1844 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001845 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001846 }
Michal Vasko428087d2016-01-14 16:04:28 +01001847
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001848 /* something happened */
1849 if (ret != NC_PSPOLL_TIMEOUT) {
1850 break;
1851 }
1852
Michal Vasko9a327362017-01-11 11:31:46 +01001853 if (i == ps->session_count - 1) {
1854 i = 0;
1855 } else {
1856 ++i;
1857 }
1858 } while (i != j);
1859
Michal Vaskoade892d2017-02-22 13:40:35 +01001860 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001861 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001862 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001863
Michal Vaskod8a74192023-02-06 15:51:50 +01001864 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001865 /* final timeout */
1866 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001867 }
Michal Vasko428087d2016-01-14 16:04:28 +01001868 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001869 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001870
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001871 /* do we want to return the session? */
1872 switch (ret) {
1873 case NC_PSPOLL_RPC:
1874 case NC_PSPOLL_SESSION_TERM:
1875 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1876#ifdef NC_ENABLED_SSH
1877 case NC_PSPOLL_SSH_CHANNEL:
1878 case NC_PSPOLL_SSH_MSG:
1879#endif
1880 if (session) {
1881 *session = cur_session;
1882 }
1883 ps->last_event_session = i;
1884 break;
1885 default:
1886 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001887 }
Michal Vasko428087d2016-01-14 16:04:28 +01001888
Michal Vaskoade892d2017-02-22 13:40:35 +01001889 /* PS UNLOCK */
1890 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001891
Michal Vasko131120a2018-05-29 15:44:02 +02001892 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001893 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001894 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001895 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1896 if (cur_session->status != NC_STATUS_RUNNING) {
1897 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001898 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001899 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001900 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001901 }
1902 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001903 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001904
Michal Vasko7f1ee932018-10-11 09:41:42 +02001905 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001906 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001907 if (cur_session->status != NC_STATUS_RUNNING) {
1908 ret |= NC_PSPOLL_SESSION_TERM;
1909 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1910 ret |= NC_PSPOLL_SESSION_ERROR;
1911 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001912 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001913 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001914 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001915 }
Michal Vasko428087d2016-01-14 16:04:28 +01001916 }
Michal Vasko77367452021-02-16 16:32:18 +01001917 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001918
Michal Vasko131120a2018-05-29 15:44:02 +02001919 /* SESSION RPC UNLOCK */
1920 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001921 }
1922
Michal Vasko48a63ed2016-03-01 09:48:21 +01001923 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001924}
1925
Michal Vaskod09eae62016-02-01 10:32:52 +01001926API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001927nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001928{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001929 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001930 uint16_t i;
1931 struct nc_session *session;
1932
Michal Vasko9a25e932016-02-01 10:36:42 +01001933 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001934 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001935 return;
1936 }
1937
Michal Vasko48a63ed2016-03-01 09:48:21 +01001938 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001939 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001940 return;
1941 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001942
Michal Vasko48a63ed2016-03-01 09:48:21 +01001943 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001944 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001945 nc_session_free(ps->sessions[i]->session, data_free);
1946 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001947 }
1948 free(ps->sessions);
1949 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001950 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001951 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001952 } else {
1953 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001954 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1955 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001956 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001957 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001958 continue;
1959 }
1960
1961 ++i;
1962 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001963 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001964
1965 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001966 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001967}
1968
Michal Vasko5f352c52019-07-10 16:12:06 +02001969static int
apropp-molex4e903c32020-04-20 03:06:58 -04001970nc_get_uid(int sock, uid_t *uid)
1971{
Michal Vaskod3910912020-04-20 09:12:49 +02001972 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04001973
Michal Vaskod3910912020-04-20 09:12:49 +02001974#ifdef SO_PEERCRED
1975 struct ucred ucred;
1976 socklen_t len;
Michal Vasko292c5542023-02-01 14:33:17 +01001977
Michal Vaskod3910912020-04-20 09:12:49 +02001978 len = sizeof(ucred);
1979 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
1980 if (!ret) {
1981 *uid = ucred.uid;
1982 }
1983#else
1984 ret = getpeereid(sock, uid, NULL);
1985#endif
1986
1987 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001988 ERR(NULL, "Failed to get credentials from unix socket (%s).", strerror(errno));
Michal Vaskod3910912020-04-20 09:12:49 +02001989 return -1;
1990 }
apropp-molex4e903c32020-04-20 03:06:58 -04001991 return 0;
1992}
1993
1994static int
Michal Vasko5f352c52019-07-10 16:12:06 +02001995nc_accept_unix(struct nc_session *session, int sock)
1996{
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001997#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001998 struct passwd *pw, pw_buf;
Michal Vasko5f352c52019-07-10 16:12:06 +02001999 char *username;
Michal Vasko292c5542023-02-01 14:33:17 +01002000
Michal Vasko5f352c52019-07-10 16:12:06 +02002001 session->ti_type = NC_TI_UNIX;
Michal Vasko143aa142021-10-01 15:31:48 +02002002 uid_t uid = 0;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02002003 char *buf = NULL;
2004 size_t buf_len = 0;
Michal Vasko5f352c52019-07-10 16:12:06 +02002005
Michal Vaskod3910912020-04-20 09:12:49 +02002006 if (nc_get_uid(sock, &uid)) {
2007 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02002008 return -1;
2009 }
2010
romanf6e32012023-04-24 15:51:26 +02002011 pw = nc_getpw(uid, NULL, &pw_buf, &buf, &buf_len);
Michal Vasko5f352c52019-07-10 16:12:06 +02002012 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02002013 ERR(NULL, "Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02002014 close(sock);
2015 return -1;
2016 }
2017
2018 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02002019 free(buf);
Michal Vasko5f352c52019-07-10 16:12:06 +02002020 if (username == NULL) {
2021 ERRMEM;
2022 close(sock);
2023 return -1;
2024 }
Michal Vasko93224072021-11-09 12:14:28 +01002025 session->username = username;
Michal Vasko5f352c52019-07-10 16:12:06 +02002026
2027 session->ti.unixsock.sock = sock;
2028
2029 return 1;
Claus Klein22091912020-01-20 13:45:47 +01002030#else
2031 return -1;
2032#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02002033}
2034
Michal Vaskoe2713da2016-08-22 16:06:40 +02002035API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002036nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01002037{
Michal Vasko3031aae2016-01-27 16:07:18 +01002038 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01002039 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01002040
Michal Vasko45e53ae2016-04-07 11:46:03 +02002041 if (!name) {
2042 ERRARG("name");
2043 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01002044 }
2045
Michal Vaskoade892d2017-02-22 13:40:35 +01002046 /* BIND LOCK */
2047 pthread_mutex_lock(&server_opts.bind_lock);
2048
2049 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002050 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002051
2052 /* check name uniqueness */
2053 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002054 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002055 ERR(NULL, "Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01002056 ret = -1;
2057 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01002058 }
2059 }
2060
Michal Vasko93224072021-11-09 12:14:28 +01002061 server_opts.endpts = nc_realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002062 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01002063 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002064 ret = -1;
2065 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002066 }
Michal Vasko93224072021-11-09 12:14:28 +01002067 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
2068 ++server_opts.endpt_count;
2069
2070 server_opts.endpts[server_opts.endpt_count - 1].name = strdup(name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002071 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002072 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
2073 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
2074 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002075
Michal Vaskoe2713da2016-08-22 16:06:40 +02002076 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002077 if (!server_opts.binds) {
2078 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002079 ret = -1;
2080 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01002081 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002082
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002083 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002084 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
2085
2086 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002087#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002088 case NC_TI_LIBSSH:
2089 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2090 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
2091 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002092 ret = -1;
2093 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002094 }
2095 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
roman41a11e42022-06-22 09:27:08 +02002096 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002097 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002098 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002099 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002100#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02002101#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002102 case NC_TI_OPENSSL:
2103 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2104 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
2105 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002106 ret = -1;
2107 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002108 }
2109 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002110#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002111 case NC_TI_UNIX:
2112 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
2113 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
2114 ERRMEM;
2115 ret = -1;
2116 goto cleanup;
2117 }
2118 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
2119 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
2120 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
2121 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002122 default:
2123 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01002124 ret = -1;
2125 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002126 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002127
Michal Vaskoade892d2017-02-22 13:40:35 +01002128cleanup:
2129 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002130 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002131
Michal Vaskoade892d2017-02-22 13:40:35 +01002132 /* BIND UNLOCK */
2133 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002134
Michal Vaskoade892d2017-02-22 13:40:35 +01002135 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002136}
2137
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002138API int
Michal Vasko59050372016-11-22 14:33:55 +01002139nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01002140{
2141 uint32_t i;
2142 int ret = -1;
2143
Michal Vaskoade892d2017-02-22 13:40:35 +01002144 /* BIND LOCK */
2145 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002146
Michal Vaskoade892d2017-02-22 13:40:35 +01002147 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002148 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002149
Michal Vasko59050372016-11-22 14:33:55 +01002150 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002151 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01002152 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002153 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002154 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002155#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002156 case NC_TI_LIBSSH:
2157 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2158 free(server_opts.endpts[i].opts.ssh);
2159 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002160#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002161#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002162 case NC_TI_OPENSSL:
2163 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2164 free(server_opts.endpts[i].opts.tls);
2165 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002166#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002167 case NC_TI_UNIX:
2168 free(server_opts.endpts[i].opts.unixsock);
2169 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002170 default:
2171 ERRINT;
2172 /* won't get here ...*/
2173 break;
2174 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002175 ret = 0;
2176 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002177 free(server_opts.endpts);
2178 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002179
2180 /* remove all binds */
2181 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002182 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002183 if (server_opts.binds[i].sock > -1) {
2184 close(server_opts.binds[i].sock);
2185 }
2186 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002187 free(server_opts.binds);
2188 server_opts.binds = NULL;
2189
Michal Vasko3031aae2016-01-27 16:07:18 +01002190 server_opts.endpt_count = 0;
2191
Michal Vasko1a38c862016-01-15 15:50:07 +01002192 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002193 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002194 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002195 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002196 /* remove endpt */
Michal Vasko93224072021-11-09 12:14:28 +01002197 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002198 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002199#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002200 case NC_TI_LIBSSH:
2201 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2202 free(server_opts.endpts[i].opts.ssh);
2203 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002204#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002205#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002206 case NC_TI_OPENSSL:
2207 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2208 free(server_opts.endpts[i].opts.tls);
2209 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002210#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002211 case NC_TI_UNIX:
2212 free(server_opts.endpts[i].opts.unixsock);
2213 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002214 default:
2215 ERRINT;
2216 break;
2217 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002218
Michal Vaskoe2713da2016-08-22 16:06:40 +02002219 /* remove bind(s) */
Michal Vasko93224072021-11-09 12:14:28 +01002220 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002221 if (server_opts.binds[i].sock > -1) {
2222 close(server_opts.binds[i].sock);
2223 }
2224
2225 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002226 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002227 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002228 free(server_opts.binds);
2229 server_opts.binds = NULL;
2230 free(server_opts.endpts);
2231 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002232 } else if (i < server_opts.endpt_count) {
2233 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2234 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002235 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002236
2237 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002238 if (name) {
2239 break;
2240 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002241 }
2242 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002243 }
2244
Michal Vaskoade892d2017-02-22 13:40:35 +01002245 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002246 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002247
Michal Vaskoade892d2017-02-22 13:40:35 +01002248 /* BIND UNLOCK */
2249 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002250
2251 return ret;
2252}
2253
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002254API int
2255nc_server_endpt_count(void)
2256{
2257 return server_opts.endpt_count;
2258}
2259
Michal Vasko1b5973e2020-01-30 16:05:46 +01002260API int
2261nc_server_is_endpt(const char *name)
2262{
2263 uint16_t i;
2264 int found = 0;
2265
Michal Vaskofb1724b2020-01-31 11:02:00 +01002266 if (!name) {
2267 return found;
2268 }
2269
Michal Vasko1b5973e2020-01-30 16:05:46 +01002270 /* ENDPT READ LOCK */
2271 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2272
2273 /* check name uniqueness */
2274 for (i = 0; i < server_opts.endpt_count; ++i) {
2275 if (!strcmp(server_opts.endpts[i].name, name)) {
2276 found = 1;
2277 break;
2278 }
2279 }
2280
2281 /* ENDPT UNLOCK */
2282 pthread_rwlock_unlock(&server_opts.endpt_lock);
2283
2284 return found;
2285}
2286
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002287int
2288nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2289{
2290 struct nc_endpt *endpt;
2291 struct nc_bind *bind = NULL;
2292 uint16_t i;
2293 int sock = -1, set_addr, ret = 0;
2294
2295 if (!endpt_name) {
2296 ERRARG("endpt_name");
2297 return -1;
2298 } else if ((!address && !port) || (address && port)) {
2299 ERRARG("address and port");
2300 return -1;
2301 }
2302
2303 if (address) {
2304 set_addr = 1;
2305 } else {
2306 set_addr = 0;
2307 }
2308
2309 /* BIND LOCK */
2310 pthread_mutex_lock(&server_opts.bind_lock);
2311
2312 /* ENDPT LOCK */
2313 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2314 if (!endpt) {
2315 /* BIND UNLOCK */
2316 pthread_mutex_unlock(&server_opts.bind_lock);
2317 return -1;
2318 }
2319
2320 bind = &server_opts.binds[i];
2321
2322 if (set_addr) {
2323 port = bind->port;
2324 } else {
2325 address = bind->address;
2326 }
2327
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002328 if (!set_addr && (endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002329 ret = -1;
2330 goto cleanup;
2331 }
2332
2333 /* we have all the information we need to create a listening socket */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002334 if (address && (port || (endpt->ti == NC_TI_UNIX))) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002335 /* create new socket, close the old one */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002336 if (endpt->ti == NC_TI_UNIX) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002337 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002338 } else {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002339 sock = nc_sock_listen_inet(address, port, &endpt->ka);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002340 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002341 if (sock == -1) {
2342 ret = -1;
2343 goto cleanup;
2344 }
2345
2346 if (bind->sock > -1) {
2347 close(bind->sock);
2348 }
2349 bind->sock = sock;
2350 } /* else we are just setting address or port */
2351
2352 if (set_addr) {
Michal Vasko93224072021-11-09 12:14:28 +01002353 free(bind->address);
2354 bind->address = strdup(address);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002355 } else {
2356 bind->port = port;
2357 }
2358
2359 if (sock > -1) {
Michal Vasko946cacb2020-08-12 11:18:08 +02002360 switch (endpt->ti) {
2361 case NC_TI_UNIX:
Michal Vasko05532772021-06-03 12:12:38 +02002362 VRB(NULL, "Listening on %s for UNIX connections.", address);
Michal Vasko946cacb2020-08-12 11:18:08 +02002363 break;
2364#ifdef NC_ENABLED_SSH
2365 case NC_TI_LIBSSH:
Michal Vasko05532772021-06-03 12:12:38 +02002366 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002367 break;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002368#endif
Michal Vasko946cacb2020-08-12 11:18:08 +02002369#ifdef NC_ENABLED_TLS
2370 case NC_TI_OPENSSL:
Michal Vasko05532772021-06-03 12:12:38 +02002371 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002372 break;
2373#endif
2374 default:
2375 ERRINT;
2376 break;
2377 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002378 }
2379
2380cleanup:
2381 /* ENDPT UNLOCK */
2382 pthread_rwlock_unlock(&server_opts.endpt_lock);
2383
2384 /* BIND UNLOCK */
2385 pthread_mutex_unlock(&server_opts.bind_lock);
2386
2387 return ret;
2388}
2389
2390API int
2391nc_server_endpt_set_address(const char *endpt_name, const char *address)
2392{
2393 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2394}
2395
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002396#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002397
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002398API int
2399nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2400{
2401 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2402}
2403
Michal Vasko946cacb2020-08-12 11:18:08 +02002404#endif
2405
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002406API int
2407nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2408{
2409 struct nc_endpt *endpt;
2410 uint16_t i;
2411 int ret = 0;
2412
2413 if (!endpt_name) {
2414 ERRARG("endpt_name");
2415 return -1;
2416 } else if (mode == 0) {
2417 ERRARG("mode");
2418 return -1;
2419 }
2420
2421 /* ENDPT LOCK */
2422 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002423 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002424 return -1;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002425 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002426
2427 if (endpt->ti != NC_TI_UNIX) {
2428 ret = -1;
2429 goto cleanup;
2430 }
2431
2432 endpt->opts.unixsock->mode = mode;
2433 endpt->opts.unixsock->uid = uid;
2434 endpt->opts.unixsock->gid = gid;
2435
2436cleanup:
2437 /* ENDPT UNLOCK */
2438 pthread_rwlock_unlock(&server_opts.endpt_lock);
2439
2440 return ret;
2441}
2442
2443API int
2444nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2445{
2446 struct nc_endpt *endpt;
2447 int ret = 0;
2448
2449 if (!endpt_name) {
2450 ERRARG("endpt_name");
2451 return -1;
2452 }
2453
2454 /* ENDPT LOCK */
2455 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2456 if (!endpt) {
2457 return -1;
2458 }
2459
2460 endpt->ka.enabled = (enable ? 1 : 0);
2461
2462 /* ENDPT UNLOCK */
2463 pthread_rwlock_unlock(&server_opts.endpt_lock);
2464
2465 return ret;
2466}
2467
2468API int
2469nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2470{
2471 struct nc_endpt *endpt;
2472 int ret = 0;
2473
2474 if (!endpt_name) {
2475 ERRARG("endpt_name");
2476 return -1;
2477 }
2478
2479 /* ENDPT LOCK */
2480 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2481 if (!endpt) {
2482 return -1;
2483 }
2484
2485 if (idle_time > -1) {
2486 endpt->ka.idle_time = idle_time;
2487 }
2488 if (max_probes > -1) {
2489 endpt->ka.max_probes = max_probes;
2490 }
2491 if (probe_interval > -1) {
2492 endpt->ka.probe_interval = probe_interval;
2493 }
2494
2495 /* ENDPT UNLOCK */
2496 pthread_rwlock_unlock(&server_opts.endpt_lock);
2497
2498 return ret;
2499}
2500
Michal Vasko71090fc2016-05-24 16:37:28 +02002501API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +01002502nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002503{
Michal Vasko71090fc2016-05-24 16:37:28 +02002504 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002505 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002506 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002507 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002508 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002509
Michal Vasko93224072021-11-09 12:14:28 +01002510 if (!ctx) {
2511 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +02002512 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002513 } else if (!session) {
2514 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002515 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002516 }
2517
Michal Vasko93224072021-11-09 12:14:28 +01002518 /* init ctx as needed */
2519 nc_server_init_ctx(ctx);
2520
Michal Vaskoade892d2017-02-22 13:40:35 +01002521 /* BIND LOCK */
2522 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002523
2524 if (!server_opts.endpt_count) {
Michal Vasko05532772021-06-03 12:12:38 +02002525 ERR(NULL, "No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002526 /* BIND UNLOCK */
2527 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002528 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002529 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002530
Michal Vaskoe2713da2016-08-22 16:06:40 +02002531 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002532 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002533 /* BIND UNLOCK */
2534 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002535 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002536 if (!ret) {
2537 return NC_MSG_WOULDBLOCK;
2538 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002539 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002540 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002541
2542 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2543 /* ENDPT READ LOCK */
2544 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2545
2546 /* BIND UNLOCK */
2547 pthread_mutex_unlock(&server_opts.bind_lock);
2548
Michal Vaskob48aa812016-01-18 14:13:09 +01002549 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002550
Michal Vasko131120a2018-05-29 15:44:02 +02002551 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002552 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002553 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002554 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002555 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002556 msgtype = NC_MSG_ERROR;
2557 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002558 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002559 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01002560 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko1a38c862016-01-15 15:50:07 +01002561 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01002562 (*session)->host = host;
Michal Vasko1a38c862016-01-15 15:50:07 +01002563 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002564
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002565 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002566#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002567 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2568 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002569 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002570 if (ret < 0) {
2571 msgtype = NC_MSG_ERROR;
2572 goto cleanup;
2573 } else if (!ret) {
2574 msgtype = NC_MSG_WOULDBLOCK;
2575 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002576 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002577 } else
2578#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002579#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002580 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2581 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002582 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002583 if (ret < 0) {
2584 msgtype = NC_MSG_ERROR;
2585 goto cleanup;
2586 } else if (!ret) {
2587 msgtype = NC_MSG_WOULDBLOCK;
2588 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002589 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002590 } else
2591#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002592 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2593 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2594 ret = nc_accept_unix(*session, sock);
2595 if (ret < 0) {
2596 msgtype = NC_MSG_ERROR;
2597 goto cleanup;
2598 }
2599 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002600 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002601 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002602 msgtype = NC_MSG_ERROR;
2603 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002604 }
2605
Michal Vasko2cc4c682016-03-01 09:16:48 +01002606 (*session)->data = NULL;
2607
Michal Vaskoade892d2017-02-22 13:40:35 +01002608 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002609 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002610
Michal Vaskob48aa812016-01-18 14:13:09 +01002611 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002612 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002613
Michal Vasko9e036d52016-01-08 10:49:26 +01002614 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002615 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002616 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002617 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002618 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002619 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002620 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002621
Michal Vaskod8a74192023-02-06 15:51:50 +01002622 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02002623 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +01002624 nc_realtime_get(&ts_cur);
Michal Vasko9fb42272017-10-05 13:50:05 +02002625 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002626 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002627
Michal Vasko71090fc2016-05-24 16:37:28 +02002628 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002629
Michal Vasko71090fc2016-05-24 16:37:28 +02002630cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002631 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002632 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002633
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002634 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002635 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002636 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002637}
2638
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002639#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002640
Michal Vaskoadf30f02019-06-24 09:34:47 +02002641/* client is expected to be locked */
2642static int
2643_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 +02002644{
2645 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002646 int ret = -1;
2647
2648 if (!endpt_name) {
2649 /* remove all endpoints */
2650 for (i = 0; i < client->ch_endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002651 free(client->ch_endpts[i].name);
2652 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002653 if (client->ch_endpts[i].sock_pending != -1) {
2654 close(client->ch_endpts[i].sock_pending);
2655 }
2656 switch (client->ch_endpts[i].ti) {
2657#ifdef NC_ENABLED_SSH
2658 case NC_TI_LIBSSH:
2659 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2660 free(client->ch_endpts[i].opts.ssh);
2661 break;
2662#endif
2663#ifdef NC_ENABLED_TLS
2664 case NC_TI_OPENSSL:
2665 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2666 free(client->ch_endpts[i].opts.tls);
2667 break;
2668#endif
2669 default:
2670 ERRINT;
2671 /* won't get here ...*/
2672 break;
2673 }
2674 }
2675 free(client->ch_endpts);
2676 client->ch_endpts = NULL;
2677 client->ch_endpt_count = 0;
2678
2679 ret = 0;
2680 } else {
2681 for (i = 0; i < client->ch_endpt_count; ++i) {
2682 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
Michal Vasko93224072021-11-09 12:14:28 +01002683 free(client->ch_endpts[i].name);
2684 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002685 if (client->ch_endpts[i].sock_pending != -1) {
2686 close(client->ch_endpts[i].sock_pending);
2687 }
2688 switch (client->ch_endpts[i].ti) {
2689#ifdef NC_ENABLED_SSH
2690 case NC_TI_LIBSSH:
2691 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2692 free(client->ch_endpts[i].opts.ssh);
2693 break;
2694#endif
2695#ifdef NC_ENABLED_TLS
2696 case NC_TI_OPENSSL:
2697 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2698 free(client->ch_endpts[i].opts.tls);
2699 break;
2700#endif
2701 default:
2702 ERRINT;
2703 /* won't get here ...*/
2704 break;
2705 }
2706
2707 /* move last endpoint to the empty space */
2708 --client->ch_endpt_count;
2709 if (i < client->ch_endpt_count) {
2710 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2711 } else if (!server_opts.ch_client_count) {
2712 free(server_opts.ch_clients);
2713 server_opts.ch_clients = NULL;
2714 }
2715
2716 ret = 0;
2717 break;
2718 }
2719 }
2720 }
2721
2722 return ret;
2723}
2724
2725API int
2726nc_server_ch_add_client(const char *name)
2727{
2728 uint16_t i;
2729 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002730
2731 if (!name) {
2732 ERRARG("name");
2733 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002734 }
2735
2736 /* WRITE LOCK */
2737 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2738
2739 /* check name uniqueness */
2740 for (i = 0; i < server_opts.ch_client_count; ++i) {
2741 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002742 ERR(NULL, "Call Home client \"%s\" already exists.", name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002743 /* WRITE UNLOCK */
2744 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2745 return -1;
2746 }
2747 }
2748
2749 ++server_opts.ch_client_count;
2750 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2751 if (!server_opts.ch_clients) {
2752 ERRMEM;
2753 /* WRITE UNLOCK */
2754 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2755 return -1;
2756 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002757 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002758
Michal Vasko93224072021-11-09 12:14:28 +01002759 client->name = strdup(name);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002760 client->id = ATOMIC_INC_RELAXED(server_opts.new_client_id);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002761 client->ch_endpts = NULL;
2762 client->ch_endpt_count = 0;
2763 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002764
Michal Vasko2e6defd2016-10-07 15:48:15 +02002765 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002766 client->start_with = NC_CH_FIRST_LISTED;
2767 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002768
Michal Vaskoadf30f02019-06-24 09:34:47 +02002769 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002770
2771 /* WRITE UNLOCK */
2772 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2773
2774 return 0;
2775}
2776
2777API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002778nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002779{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002780 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002781 int ret = -1;
2782
2783 /* WRITE LOCK */
2784 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2785
Michal Vaskoadf30f02019-06-24 09:34:47 +02002786 if (!name) {
2787 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002788 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002789 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002790
2791 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002792 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002793
2794 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002795 ret = 0;
2796 }
2797 free(server_opts.ch_clients);
2798 server_opts.ch_clients = NULL;
2799
2800 server_opts.ch_client_count = 0;
2801
2802 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002803 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002804 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002805 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko93224072021-11-09 12:14:28 +01002806 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002807
Michal Vasko2e6defd2016-10-07 15:48:15 +02002808 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002809 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002810
2811 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2812
2813 /* move last client and endpoint(s) to the empty space */
2814 --server_opts.ch_client_count;
2815 if (i < server_opts.ch_client_count) {
2816 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002817 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002818 } else if (!server_opts.ch_client_count) {
2819 free(server_opts.ch_clients);
2820 server_opts.ch_clients = NULL;
2821 }
2822
2823 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002824 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002825 }
2826 }
2827 }
2828
2829 /* WRITE UNLOCK */
2830 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2831
2832 return ret;
2833}
2834
2835API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002836nc_server_ch_is_client(const char *name)
2837{
2838 uint16_t i;
2839 int found = 0;
2840
2841 if (!name) {
2842 return found;
2843 }
2844
2845 /* READ LOCK */
2846 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2847
2848 /* check name uniqueness */
2849 for (i = 0; i < server_opts.ch_client_count; ++i) {
2850 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2851 found = 1;
2852 break;
2853 }
2854 }
2855
2856 /* UNLOCK */
2857 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2858
2859 return found;
2860}
2861
2862API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002863nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002864{
2865 uint16_t i;
2866 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002867 struct nc_ch_endpt *endpt;
2868 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002869
2870 if (!client_name) {
2871 ERRARG("client_name");
2872 return -1;
2873 } else if (!endpt_name) {
2874 ERRARG("endpt_name");
2875 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002876 } else if (!ti) {
2877 ERRARG("ti");
2878 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002879 }
2880
2881 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002882 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002883 if (!client) {
2884 return -1;
2885 }
2886
2887 for (i = 0; i < client->ch_endpt_count; ++i) {
2888 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002889 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002890 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002891 }
2892 }
2893
2894 ++client->ch_endpt_count;
2895 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2896 if (!client->ch_endpts) {
2897 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002898 goto cleanup;
2899 }
2900 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2901
2902 memset(endpt, 0, sizeof *client->ch_endpts);
Michal Vasko93224072021-11-09 12:14:28 +01002903 endpt->name = strdup(endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002904 endpt->ti = ti;
2905 endpt->sock_pending = -1;
2906 endpt->ka.idle_time = 1;
2907 endpt->ka.max_probes = 10;
2908 endpt->ka.probe_interval = 5;
2909
2910 switch (ti) {
2911#ifdef NC_ENABLED_SSH
2912 case NC_TI_LIBSSH:
2913 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2914 if (!endpt->opts.ssh) {
2915 ERRMEM;
2916 goto cleanup;
2917 }
roman41a11e42022-06-22 09:27:08 +02002918 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002919 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002920 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002921 break;
2922#endif
2923#ifdef NC_ENABLED_TLS
2924 case NC_TI_OPENSSL:
2925 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2926 if (!endpt->opts.tls) {
2927 ERRMEM;
2928 goto cleanup;
2929 }
2930 break;
2931#endif
2932 default:
2933 ERRINT;
2934 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002935 }
2936
Michal Vaskoadf30f02019-06-24 09:34:47 +02002937 /* success */
2938 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002939
Michal Vaskoadf30f02019-06-24 09:34:47 +02002940cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002941 /* UNLOCK */
2942 nc_server_ch_client_unlock(client);
2943
Michal Vaskoadf30f02019-06-24 09:34:47 +02002944 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002945}
2946
2947API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002948nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002949{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002950 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002951 struct nc_ch_client *client;
2952
2953 if (!client_name) {
2954 ERRARG("client_name");
2955 return -1;
2956 }
2957
2958 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002959 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002960 if (!client) {
2961 return -1;
2962 }
2963
Michal Vaskoadf30f02019-06-24 09:34:47 +02002964 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002965
2966 /* UNLOCK */
2967 nc_server_ch_client_unlock(client);
2968
2969 return ret;
2970}
2971
2972API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002973nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2974{
2975 uint16_t i;
2976 struct nc_ch_client *client = NULL;
2977 int found = 0;
2978
2979 if (!client_name || !endpt_name) {
2980 return found;
2981 }
2982
2983 /* READ LOCK */
2984 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2985
2986 for (i = 0; i < server_opts.ch_client_count; ++i) {
2987 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2988 client = &server_opts.ch_clients[i];
2989 break;
2990 }
2991 }
2992
2993 if (!client) {
2994 goto cleanup;
2995 }
2996
2997 for (i = 0; i < client->ch_endpt_count; ++i) {
2998 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2999 found = 1;
3000 break;
3001 }
3002 }
3003
3004cleanup:
3005 /* UNLOCK */
3006 pthread_rwlock_unlock(&server_opts.ch_client_lock);
3007 return found;
3008}
3009
3010API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003011nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
3012{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003013 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003014 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003015
3016 if (!client_name) {
3017 ERRARG("client_name");
3018 return -1;
3019 } else if (!endpt_name) {
3020 ERRARG("endpt_name");
3021 return -1;
3022 } else if (!address) {
3023 ERRARG("address");
3024 return -1;
3025 }
3026
3027 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003028 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3029 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003030 return -1;
3031 }
3032
Michal Vasko93224072021-11-09 12:14:28 +01003033 free(endpt->address);
3034 endpt->address = strdup(address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003035
3036 /* UNLOCK */
3037 nc_server_ch_client_unlock(client);
3038
Michal Vaskoadf30f02019-06-24 09:34:47 +02003039 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003040}
3041
3042API int
3043nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
3044{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003045 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003046 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003047
3048 if (!client_name) {
3049 ERRARG("client_name");
3050 return -1;
3051 } else if (!endpt_name) {
3052 ERRARG("endpt_name");
3053 return -1;
3054 } else if (!port) {
3055 ERRARG("port");
3056 return -1;
3057 }
3058
3059 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003060 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3061 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003062 return -1;
3063 }
3064
Michal Vaskoadf30f02019-06-24 09:34:47 +02003065 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003066
3067 /* UNLOCK */
3068 nc_server_ch_client_unlock(client);
3069
ravsz5c5a4422020-03-31 15:53:21 +02003070 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003071}
3072
3073API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003074nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
3075{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003076 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003077 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003078
3079 if (!client_name) {
3080 ERRARG("client_name");
3081 return -1;
3082 } else if (!endpt_name) {
3083 ERRARG("endpt_name");
3084 return -1;
3085 }
3086
3087 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003088 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3089 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003090 return -1;
3091 }
3092
Michal Vaskoadf30f02019-06-24 09:34:47 +02003093 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003094
3095 /* UNLOCK */
3096 nc_server_ch_client_unlock(client);
3097
Michal Vasko9af829a2019-09-12 13:50:00 +02003098 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003099}
3100
3101API int
3102nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
3103 int probe_interval)
3104{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003105 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003106 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003107
3108 if (!client_name) {
3109 ERRARG("client_name");
3110 return -1;
3111 } else if (!endpt_name) {
3112 ERRARG("endpt_name");
3113 return -1;
3114 }
3115
3116 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003117 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3118 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003119 return -1;
3120 }
3121
Michal Vaskoadf30f02019-06-24 09:34:47 +02003122 if (idle_time > -1) {
3123 endpt->ka.idle_time = idle_time;
3124 }
3125 if (max_probes > -1) {
3126 endpt->ka.max_probes = max_probes;
3127 }
3128 if (probe_interval > -1) {
3129 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003130 }
3131
3132 /* UNLOCK */
3133 nc_server_ch_client_unlock(client);
3134
Michal Vasko9af829a2019-09-12 13:50:00 +02003135 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003136}
3137
3138API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003139nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
3140{
3141 struct nc_ch_client *client;
3142
3143 if (!client_name) {
3144 ERRARG("client_name");
3145 return -1;
3146 } else if (!conn_type) {
3147 ERRARG("conn_type");
3148 return -1;
3149 }
3150
3151 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003152 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003153 if (!client) {
3154 return -1;
3155 }
3156
3157 if (client->conn_type != conn_type) {
3158 client->conn_type = conn_type;
3159
3160 /* set default options */
3161 switch (conn_type) {
3162 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003163 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003164 break;
3165 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003166 client->conn.period.period = 60;
3167 client->conn.period.anchor_time = 0;
3168 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003169 break;
3170 default:
3171 ERRINT;
3172 break;
3173 }
3174 }
3175
3176 /* UNLOCK */
3177 nc_server_ch_client_unlock(client);
3178
3179 return 0;
3180}
3181
3182API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003183nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
3184{
3185 struct nc_ch_client *client;
3186
3187 if (!client_name) {
3188 ERRARG("client_name");
3189 return -1;
3190 } else if (!period) {
3191 ERRARG("period");
3192 return -1;
3193 }
3194
3195 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003196 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003197 if (!client) {
3198 return -1;
3199 }
3200
3201 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003202 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003203 /* UNLOCK */
3204 nc_server_ch_client_unlock(client);
3205 return -1;
3206 }
3207
3208 client->conn.period.period = period;
3209
3210 /* UNLOCK */
3211 nc_server_ch_client_unlock(client);
3212
3213 return 0;
3214}
3215
3216API int
3217nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003218{
3219 struct nc_ch_client *client;
3220
3221 if (!client_name) {
3222 ERRARG("client_name");
3223 return -1;
3224 }
3225
3226 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003227 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003228 if (!client) {
3229 return -1;
3230 }
3231
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003232 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003233 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003234 /* UNLOCK */
3235 nc_server_ch_client_unlock(client);
3236 return -1;
3237 }
3238
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003239 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003240
3241 /* UNLOCK */
3242 nc_server_ch_client_unlock(client);
3243
3244 return 0;
3245}
3246
3247API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003248nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003249{
3250 struct nc_ch_client *client;
3251
3252 if (!client_name) {
3253 ERRARG("client_name");
3254 return -1;
3255 }
3256
3257 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003258 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003259 if (!client) {
3260 return -1;
3261 }
3262
3263 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003264 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003265 /* UNLOCK */
3266 nc_server_ch_client_unlock(client);
3267 return -1;
3268 }
3269
3270 client->conn.period.idle_timeout = idle_timeout;
3271
3272 /* UNLOCK */
3273 nc_server_ch_client_unlock(client);
3274
3275 return 0;
3276}
3277
3278API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003279nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
3280{
3281 struct nc_ch_client *client;
3282
3283 if (!client_name) {
3284 ERRARG("client_name");
3285 return -1;
3286 }
3287
3288 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003289 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003290 if (!client) {
3291 return -1;
3292 }
3293
3294 client->start_with = start_with;
3295
3296 /* UNLOCK */
3297 nc_server_ch_client_unlock(client);
3298
3299 return 0;
3300}
3301
3302API int
3303nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3304{
3305 struct nc_ch_client *client;
3306
3307 if (!client_name) {
3308 ERRARG("client_name");
3309 return -1;
3310 } else if (!max_attempts) {
3311 ERRARG("max_attempts");
3312 return -1;
3313 }
3314
3315 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003316 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003317 if (!client) {
3318 return -1;
3319 }
3320
3321 client->max_attempts = max_attempts;
3322
3323 /* UNLOCK */
3324 nc_server_ch_client_unlock(client);
3325
3326 return 0;
3327}
3328
Michal Vasko056f53c2022-10-21 13:38:15 +02003329/**
3330 * @brief Create a connection for an endpoint.
3331 *
3332 * Client lock is expected to be held.
3333 *
3334 * @param[in] endpt Endpoint to use.
3335 * @param[in] acquire_ctx_cb Callback for acquiring the libyang context.
3336 * @param[in] release_ctx_cb Callback for releasing the libyang context.
3337 * @param[in] ctx_cb_data Context callbacks data.
3338 * @param[out] session Created NC session.
3339 * @return NC_MSG values.
3340 */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003341static NC_MSG_TYPE
Michal Vasko58bac1c2022-03-24 15:25:26 +01003342nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
3343 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 +01003344{
Michal Vasko71090fc2016-05-24 16:37:28 +02003345 NC_MSG_TYPE msgtype;
Michal Vasko58bac1c2022-03-24 15:25:26 +01003346 const struct ly_ctx *ctx = NULL;
Michal Vaskob05053d2016-01-22 16:12:06 +01003347 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003348 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003349 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003350
Michal Vasko056f53c2022-10-21 13:38:15 +02003351 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 +01003352 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02003353 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003354 }
3355
Michal Vasko93224072021-11-09 12:14:28 +01003356 /* acquire context */
3357 ctx = acquire_ctx_cb(ctx_cb_data);
3358 if (!ctx) {
3359 ERR(NULL, "Failed to acquire context for a new Call Home session.");
3360 close(sock);
3361 free(ip_host);
3362 return NC_MSG_ERROR;
3363 }
3364
3365 /* create session */
Michal Vasko131120a2018-05-29 15:44:02 +02003366 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003367 if (!(*session)) {
3368 ERRMEM;
3369 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003370 free(ip_host);
Michal Vaskod530d362022-09-07 10:07:57 +02003371 msgtype = NC_MSG_ERROR;
3372 goto fail;
Michal Vaskob05053d2016-01-22 16:12:06 +01003373 }
3374 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01003375 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vaskodc96bb92023-03-28 08:52:48 +02003376 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vasko93224072021-11-09 12:14:28 +01003377 (*session)->host = ip_host;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003378 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003379
Michal Vaskob05053d2016-01-22 16:12:06 +01003380 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003381#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003382 if (endpt->ti == NC_TI_LIBSSH) {
3383 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003384 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003385 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003386
Michal Vasko71090fc2016-05-24 16:37:28 +02003387 if (ret < 0) {
3388 msgtype = NC_MSG_ERROR;
3389 goto fail;
3390 } else if (!ret) {
3391 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003392 goto fail;
3393 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003394 } else
3395#endif
Radek Krejci53691be2016-02-22 13:58:37 +01003396#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003397 if (endpt->ti == NC_TI_OPENSSL) {
3398 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003399 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003400 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003401
Michal Vasko71090fc2016-05-24 16:37:28 +02003402 if (ret < 0) {
3403 msgtype = NC_MSG_ERROR;
3404 goto fail;
3405 } else if (!ret) {
3406 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003407 goto fail;
3408 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003409 } else
3410#endif
3411 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003412 ERRINT;
3413 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003414 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003415 goto fail;
3416 }
3417
3418 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02003419 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003420
3421 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003422 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003423 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003424 goto fail;
3425 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003426
Michal Vaskod8a74192023-02-06 15:51:50 +01003427 nc_timeouttime_get(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02003428 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskod8a74192023-02-06 15:51:50 +01003429 nc_realtime_get(&ts_cur);
Michal Vasko9fb42272017-10-05 13:50:05 +02003430 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003431 (*session)->status = NC_STATUS_RUNNING;
3432
Michal Vasko71090fc2016-05-24 16:37:28 +02003433 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003434
3435fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003436 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003437 *session = NULL;
Michal Vasko58bac1c2022-03-24 15:25:26 +01003438 if (ctx) {
3439 release_ctx_cb(ctx_cb_data);
3440 }
Michal Vasko71090fc2016-05-24 16:37:28 +02003441 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003442}
3443
Michal Vasko2e6defd2016-10-07 15:48:15 +02003444struct nc_ch_client_thread_arg {
3445 char *client_name;
Michal Vasko93224072021-11-09 12:14:28 +01003446 nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb;
3447 nc_server_ch_session_release_ctx_cb release_ctx_cb;
3448 void *ctx_cb_data;
3449 nc_server_ch_new_session_cb new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003450};
3451
3452static struct nc_ch_client *
3453nc_server_ch_client_with_endpt_lock(const char *name)
3454{
3455 struct nc_ch_client *client;
3456
3457 while (1) {
3458 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003459 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003460 if (!client) {
3461 return NULL;
3462 }
3463 if (client->ch_endpt_count) {
3464 return client;
3465 }
3466 /* no endpoints defined yet */
3467
3468 /* UNLOCK */
3469 nc_server_ch_client_unlock(client);
3470
3471 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3472 }
3473
3474 return NULL;
3475}
3476
3477static int
3478nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3479{
Michal Vasko3f05a092018-03-13 10:39:49 +01003480 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003481 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003482 struct timespec ts;
3483 struct nc_ch_client *client;
3484
Michal Vasko2e6defd2016-10-07 15:48:15 +02003485 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003486 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003487
Michal Vaskofeccb312022-03-24 15:24:59 +01003488 session->flags |= NC_SESSION_CH_THREAD;
Michal Vasko0db3db52021-03-03 10:45:42 +01003489
Michal Vasko2e6defd2016-10-07 15:48:15 +02003490 /* give the session to the user */
Michal Vasko93224072021-11-09 12:14:28 +01003491 if (data->new_session_cb(data->client_name, session)) {
Michal Vaskof1c26c22021-04-12 16:34:33 +02003492 /* something is wrong, free the session */
Michal Vaskofeccb312022-03-24 15:24:59 +01003493 session->flags &= ~NC_SESSION_CH_THREAD;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003494
3495 /* CH UNLOCK */
3496 pthread_mutex_unlock(&session->opts.server.ch_lock);
3497
Michal Vasko77d56d72022-09-07 10:30:48 +02003498 /* session terminated, free it and release its context */
Michal Vaskof1c26c22021-04-12 16:34:33 +02003499 nc_session_free(session, NULL);
Michal Vasko58bac1c2022-03-24 15:25:26 +01003500 data->release_ctx_cb(data->ctx_cb_data);
3501 return ret;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003502 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003503
3504 do {
Michal Vaskod8a74192023-02-06 15:51:50 +01003505 nc_timeouttime_get(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003506
Michal Vasko0db3db52021-03-03 10:45:42 +01003507 /* CH COND WAIT */
Michal Vaskod8a74192023-02-06 15:51:50 +01003508 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 +01003509 if (!r) {
3510 /* we were woken up, something probably happened */
3511 if (session->status != NC_STATUS_RUNNING) {
3512 break;
3513 }
3514 } else if (r != ETIMEDOUT) {
Michal Vasko05532772021-06-03 12:12:38 +02003515 ERR(session, "Pthread condition timedwait failed (%s).", strerror(r));
Michal Vasko3f05a092018-03-13 10:39:49 +01003516 ret = -1;
3517 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003518 }
3519
Michal Vasko2e6defd2016-10-07 15:48:15 +02003520 /* check whether the client was not removed */
3521 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003522 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003523 if (!client) {
3524 /* client was removed, finish thread */
Michal Vasko05532772021-06-03 12:12:38 +02003525 VRB(session, "Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003526 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003527 ret = 1;
3528 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003529 }
3530
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003531 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003532 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003533 } else {
3534 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003535 }
3536
Michal Vaskod8a74192023-02-06 15:51:50 +01003537 nc_timeouttime_get(&ts, 0);
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003538 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 +02003539 VRB(session, "Call Home client \"%s\": session idle timeout elapsed.", client->name);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003540 session->status = NC_STATUS_INVALID;
3541 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3542 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003543
3544 /* UNLOCK */
3545 nc_server_ch_client_unlock(client);
3546
3547 } while (session->status == NC_STATUS_RUNNING);
3548
Michal Vaskofeccb312022-03-24 15:24:59 +01003549 /* signal to nc_session_free() that CH thread is terminating */
3550 session->flags &= ~NC_SESSION_CH_THREAD;
3551 pthread_cond_signal(&session->opts.server.ch_cond);
Michal Vasko0db3db52021-03-03 10:45:42 +01003552
Michal Vasko27377422018-03-15 08:59:35 +01003553 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003554 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01003555
Michal Vasko3f05a092018-03-13 10:39:49 +01003556 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003557}
3558
3559static void *
3560nc_ch_client_thread(void *arg)
3561{
3562 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3563 NC_MSG_TYPE msgtype;
3564 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003565 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003566 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003567 struct nc_ch_endpt *cur_endpt;
3568 struct nc_session *session;
3569 struct nc_ch_client *client;
Michal Vasko5d0a1e32022-09-07 07:46:36 +02003570 uint32_t client_id, reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003571
3572 /* LOCK */
3573 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3574 if (!client) {
3575 goto cleanup;
3576 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003577 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003578
3579 cur_endpt = &client->ch_endpts[0];
3580 cur_endpt_name = strdup(cur_endpt->name);
3581
3582 while (1) {
Michal Vasko056f53c2022-10-21 13:38:15 +02003583 if (!cur_attempts) {
3584 VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" connecting...", data->client_name, cur_endpt_name);
3585 }
Michal Vasko58bac1c2022-03-24 15:25:26 +01003586 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 +02003587
3588 if (msgtype == NC_MSG_HELLO) {
3589 /* UNLOCK */
3590 nc_server_ch_client_unlock(client);
3591
Michal Vasko05532772021-06-03 12:12:38 +02003592 VRB(NULL, "Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003593 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3594 goto cleanup;
3595 }
Michal Vasko05532772021-06-03 12:12:38 +02003596 VRB(NULL, "Call Home client \"%s\" session terminated.", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003597
3598 /* LOCK */
3599 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3600 if (!client) {
3601 goto cleanup;
3602 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003603 if (client->id != client_id) {
3604 nc_server_ch_client_unlock(client);
3605 goto cleanup;
3606 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003607
3608 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003609 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003610 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko18e1fa02021-11-29 09:02:05 +01003611 if (client->conn.period.anchor_time) {
3612 /* anchored */
3613 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3614 } else {
3615 /* fixed timeout */
3616 reconnect_in = client->conn.period.period * 60;
3617 }
3618
Michal Vasko2e6defd2016-10-07 15:48:15 +02003619 /* UNLOCK */
3620 nc_server_ch_client_unlock(client);
3621
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003622 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
Michal Vasko75b836e2022-09-07 07:51:45 +02003623 VRB(NULL, "Call Home client \"%s\" reconnecting in %" PRIu32 " seconds.", data->client_name, reconnect_in);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003624 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003625
3626 /* LOCK */
3627 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3628 if (!client) {
3629 goto cleanup;
3630 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003631 if (client->id != client_id) {
3632 nc_server_ch_client_unlock(client);
3633 goto cleanup;
3634 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003635 }
3636
3637 /* set next endpoint to try */
3638 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003639 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003640 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003641 /* we keep the current one but due to unlock/lock we have to find it again */
3642 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3643 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3644 break;
3645 }
3646 }
3647 if (next_endpt_index >= client->ch_endpt_count) {
3648 /* endpoint was removed, start with the first one */
3649 next_endpt_index = 0;
3650 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003651 } else {
3652 /* just get a random index */
3653 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003654 }
3655
Michal Vasko2e6defd2016-10-07 15:48:15 +02003656 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003657 /* UNLOCK */
3658 nc_server_ch_client_unlock(client);
3659
Michal Vasko2e6defd2016-10-07 15:48:15 +02003660 /* session was not created */
Michal Vaskod8951c72022-09-26 09:39:29 +02003661 sleep(NC_CH_ENDPT_BACKOFF_WAIT);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003662
Michal Vasko6bb116b2016-10-26 13:53:46 +02003663 /* LOCK */
3664 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3665 if (!client) {
3666 goto cleanup;
3667 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003668 if (client->id != client_id) {
3669 nc_server_ch_client_unlock(client);
3670 goto cleanup;
3671 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003672
Michal Vasko2e6defd2016-10-07 15:48:15 +02003673 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003674
3675 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003676 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3677 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003678 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003679 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003680 }
3681
Peter Feiged05f2252018-09-03 08:09:47 +00003682 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003683 /* endpoint was removed, start with the first one */
Michal Vasko056f53c2022-10-21 13:38:15 +02003684 VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" removed.", data->client_name, cur_endpt_name);
Peter Feiged05f2252018-09-03 08:09:47 +00003685 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003686 cur_attempts = 0;
3687 } else if (cur_attempts == client->max_attempts) {
3688 /* we have tried to connect to this endpoint enough times */
Michal Vasko056f53c2022-10-21 13:38:15 +02003689 VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" failed connection attempt limit %" PRIu8 " reached.",
3690 data->client_name, cur_endpt_name, client->max_attempts);
3691
3692 /* clear a pending socket, if any */
3693 cur_endpt = &client->ch_endpts[next_endpt_index];
3694 if (cur_endpt->sock_pending > -1) {
3695 close(cur_endpt->sock_pending);
3696 cur_endpt->sock_pending = -1;
3697 }
3698
Peter Feiged05f2252018-09-03 08:09:47 +00003699 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003700 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003701 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003702 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003703 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003704 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003705 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003706 cur_attempts = 0;
3707 } /* else we keep the current one */
3708 }
Peter Feiged05f2252018-09-03 08:09:47 +00003709
3710 cur_endpt = &client->ch_endpts[next_endpt_index];
3711 free(cur_endpt_name);
3712 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003713 }
3714
3715cleanup:
Michal Vasko05532772021-06-03 12:12:38 +02003716 VRB(NULL, "Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003717 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003718 free(data->client_name);
3719 free(data);
3720 return NULL;
3721}
3722
3723API int
Michal Vasko93224072021-11-09 12:14:28 +01003724nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
3725 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 +01003726{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003727 int ret;
3728 pthread_t tid;
3729 struct nc_ch_client_thread_arg *arg;
3730
3731 if (!client_name) {
3732 ERRARG("client_name");
3733 return -1;
Michal Vasko93224072021-11-09 12:14:28 +01003734 } else if (!acquire_ctx_cb) {
3735 ERRARG("acquire_ctx_cb");
3736 return -1;
3737 } else if (!release_ctx_cb) {
3738 ERRARG("release_ctx_cb");
3739 return -1;
3740 } else if (!new_session_cb) {
3741 ERRARG("new_session_cb");
Michal Vasko2e6defd2016-10-07 15:48:15 +02003742 return -1;
3743 }
3744
3745 arg = malloc(sizeof *arg);
3746 if (!arg) {
3747 ERRMEM;
3748 return -1;
3749 }
3750 arg->client_name = strdup(client_name);
3751 if (!arg->client_name) {
3752 ERRMEM;
3753 free(arg);
3754 return -1;
3755 }
Michal Vasko93224072021-11-09 12:14:28 +01003756 arg->acquire_ctx_cb = acquire_ctx_cb;
3757 arg->release_ctx_cb = release_ctx_cb;
3758 arg->ctx_cb_data = ctx_cb_data;
3759 arg->new_session_cb = new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003760
3761 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3762 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02003763 ERR(NULL, "Creating a new thread failed (%s).", strerror(ret));
Michal Vasko2e6defd2016-10-07 15:48:15 +02003764 free(arg->client_name);
3765 free(arg);
3766 return -1;
3767 }
3768 /* the thread now manages arg */
3769
3770 pthread_detach(tid);
3771
3772 return 0;
3773}
3774
Radek Krejci53691be2016-02-22 13:58:37 +01003775#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003776
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003777API time_t
3778nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003779{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003780 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003781 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003782 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003783 }
3784
Michal Vasko2e6defd2016-10-07 15:48:15 +02003785 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003786}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003787
3788API void
Michal Vasko71dbd772021-03-23 14:08:37 +01003789nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01003790{
3791 if (!session || (session->side != NC_SERVER)) {
3792 ERRARG("session");
3793 return;
3794 }
3795
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003796 /* NTF STATUS LOCK */
3797 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
3798
Michal Vasko71dbd772021-03-23 14:08:37 +01003799 ++session->opts.server.ntf_status;
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003800
3801 /* NTF STATUS UNLOCK */
3802 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko71dbd772021-03-23 14:08:37 +01003803}
3804
3805API void
3806nc_session_dec_notif_status(struct nc_session *session)
3807{
3808 if (!session || (session->side != NC_SERVER)) {
3809 ERRARG("session");
3810 return;
3811 }
3812
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003813 /* NTF STATUS LOCK */
3814 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
3815
Michal Vasko71dbd772021-03-23 14:08:37 +01003816 if (session->opts.server.ntf_status) {
3817 --session->opts.server.ntf_status;
3818 }
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003819
3820 /* NTF STATUS UNLOCK */
3821 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003822}
3823
3824API int
3825nc_session_get_notif_status(const struct nc_session *session)
3826{
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003827 uint32_t ntf_status;
3828
Michal Vasko3486a7c2017-03-03 13:28:07 +01003829 if (!session || (session->side != NC_SERVER)) {
3830 ERRARG("session");
3831 return 0;
3832 }
3833
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003834 /* NTF STATUS LOCK */
3835 pthread_mutex_lock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
3836
3837 ntf_status = session->opts.server.ntf_status;
3838
3839 /* NTF STATUS UNLOCK */
3840 pthread_mutex_unlock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
3841
3842 return ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003843}