blob: 9c1698e65b69893d8727dc5d153b69165f08af42 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 server session manipulation functions
5 *
Michal Vasko18aeb5d2017-02-17 09:23:56 +01006 * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
apropp-molex4e903c32020-04-20 03:06:58 -040014#define _QNX_SOURCE /* getpeereid */
Olivier Matzac7fa2f2018-10-11 10:02:04 +020015#define _GNU_SOURCE /* signals, threads, SO_PEERCRED */
Michal Vasko086311b2016-01-08 09:53:11 +010016
17#include <stdint.h>
18#include <stdlib.h>
19#include <errno.h>
20#include <string.h>
21#include <poll.h>
22#include <sys/types.h>
23#include <sys/socket.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020024#include <sys/un.h>
25#include <netinet/in.h>
26#include <netinet/tcp.h>
Michal Vasko086311b2016-01-08 09:53:11 +010027#include <arpa/inet.h>
28#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010029#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010030#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010031#include <time.h>
Michal Vaskoade892d2017-02-22 13:40:35 +010032#include <signal.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020033#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010034
Michal Vasko1a38c862016-01-15 15:50:07 +010035#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010036#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020037#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010038
Michal Vaskob48aa812016-01-18 14:13:09 +010039struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010040#ifdef NC_ENABLED_SSH
41 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
42#endif
43 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020044 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
45 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010046};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010047
fanchanghu966f2de2016-07-21 02:28:57 -040048static nc_rpc_clb global_rpc_clb = NULL;
49
Michal Vasko3031aae2016-01-27 16:07:18 +010050struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010051nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010052{
53 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010054 struct nc_endpt *endpt = NULL;
55
Michal Vaskoddce1212019-05-24 09:58:49 +020056 if (!name) {
57 ERRARG("endpt_name");
58 return NULL;
59 }
60
Michal Vaskoade892d2017-02-22 13:40:35 +010061 /* WRITE LOCK */
62 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010063
64 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020065 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010066 endpt = &server_opts.endpts[i];
67 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010068 }
69 }
70
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010071 if (!endpt) {
72 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010073 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020074 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010075 return NULL;
76 }
77
Michal Vaskoe2713da2016-08-22 16:06:40 +020078 if (idx) {
79 *idx = i;
80 }
81
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010082 return endpt;
83}
84
Michal Vaskoadf30f02019-06-24 09:34:47 +020085struct nc_ch_endpt *
86nc_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 +010087{
Michal Vaskoadf30f02019-06-24 09:34:47 +020088 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020089 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020090 struct nc_ch_endpt *endpt = NULL;
91
92 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +020093
Michal Vaskoddce1212019-05-24 09:58:49 +020094 if (!name) {
95 ERRARG("client_name");
96 return NULL;
97 }
98
Michal Vasko2e6defd2016-10-07 15:48:15 +020099 /* READ LOCK */
100 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
101
102 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200103 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200104 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200105 if (!endpt_name && !ti) {
106 /* return only client */
107 break;
108 }
109 for (j = 0; j < client->ch_endpt_count; ++j) {
110 if (!strcmp(client->ch_endpts[j].name, endpt_name) && (!ti || (ti == client->ch_endpts[j].ti))) {
111 endpt = &client->ch_endpts[j];
112 break;
113 }
114 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200115 break;
116 }
117 }
118
119 if (!client) {
120 ERR("Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200121
Michal Vasko2e6defd2016-10-07 15:48:15 +0200122 /* READ UNLOCK */
123 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200124 } else if (endpt_name && ti && !endpt) {
125 ERR("Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
126
127 /* READ UNLOCK */
128 pthread_rwlock_unlock(&server_opts.ch_client_lock);
129 } else {
130 /* CH CLIENT LOCK */
131 pthread_mutex_lock(&client->lock);
132
133 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200134 }
135
Michal Vaskoadf30f02019-06-24 09:34:47 +0200136 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200137}
138
139void
140nc_server_ch_client_unlock(struct nc_ch_client *client)
141{
142 /* CH CLIENT UNLOCK */
143 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100144
145 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200146 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100147}
Michal Vasko086311b2016-01-08 09:53:11 +0100148
Michal Vasko1a38c862016-01-15 15:50:07 +0100149API void
150nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
151{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200152 if (!session) {
153 ERRARG("session");
154 return;
155 } else if (!reason) {
156 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100157 return;
158 }
159
Michal Vasko142cfea2017-08-07 10:12:11 +0200160 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
161 session->killed_by = 0;
162 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100163 session->term_reason = reason;
164}
165
Michal Vasko142cfea2017-08-07 10:12:11 +0200166API void
167nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
168{
169 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
170 ERRARG("session");
171 return;
172 } else if (!sid) {
173 ERRARG("sid");
174 return;
175 }
176
177 session->killed_by = sid;
178}
179
180API void
181nc_session_set_status(struct nc_session *session, NC_STATUS status)
182{
183 if (!session) {
184 ERRARG("session");
185 return;
186 } else if (!status) {
187 ERRARG("status");
188 return;
189 }
190
191 session->status = status;
192}
193
Michal Vasko086311b2016-01-08 09:53:11 +0100194int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200195nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100196{
Michal Vasko06c860d2018-07-09 16:08:52 +0200197 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100198 int is_ipv4, sock;
199 struct sockaddr_storage saddr;
200
201 struct sockaddr_in *saddr4;
202 struct sockaddr_in6 *saddr6;
203
204
205 if (!strchr(address, ':')) {
206 is_ipv4 = 1;
207 } else {
208 is_ipv4 = 0;
209 }
210
211 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
212 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100213 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100214 goto fail;
215 }
216
Michal Vaskobe52dc22018-10-17 09:28:17 +0200217 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200218 opt = 1;
219 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
220 ERR("Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
221 goto fail;
222 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100223 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
224 ERR("Could not set TCP_NODELAY socket option (%s).", strerror(errno));
225 goto fail;
226 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200227
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200228 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100229 goto fail;
230 }
231
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200232 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100233 if (is_ipv4) {
234 saddr4 = (struct sockaddr_in *)&saddr;
235
236 saddr4->sin_family = AF_INET;
237 saddr4->sin_port = htons(port);
238
239 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100240 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100241 goto fail;
242 }
243
244 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100245 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100246 goto fail;
247 }
248
249 } else {
250 saddr6 = (struct sockaddr_in6 *)&saddr;
251
252 saddr6->sin6_family = AF_INET6;
253 saddr6->sin6_port = htons(port);
254
255 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100256 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100257 goto fail;
258 }
259
260 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100261 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100262 goto fail;
263 }
264 }
265
Michal Vaskofb89d772016-01-08 12:25:35 +0100266 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100267 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100268 goto fail;
269 }
270
271 return sock;
272
273fail:
274 if (sock > -1) {
275 close(sock);
276 }
277
278 return -1;
279}
280
281int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200282nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
283{
284 struct sockaddr_un sun;
285 int sock = -1;
286
287 sock = socket(AF_UNIX, SOCK_STREAM, 0);
288 if (sock == -1) {
289 ERR("Failed to create socket (%s).", strerror(errno));
290 goto fail;
291 }
292
293 memset(&sun, 0, sizeof(sun));
294 sun.sun_family = AF_UNIX;
295 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
296
297 unlink(sun.sun_path);
298 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
299 ERR("Could not bind \"%s\" (%s).", address, strerror(errno));
300 goto fail;
301 }
302
303 if (opts->mode != (mode_t)-1) {
304 if (chmod(sun.sun_path, opts->mode) < 0) {
305 ERR("Failed to set unix socket permissions (%s).", strerror(errno));
306 goto fail;
307 }
308 }
309
310 if (opts->uid != (uid_t)-1 || opts->gid != (gid_t)-1) {
311 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
312 ERR("Failed to set unix socket uid/gid (%s).", strerror(errno));
313 goto fail;
314 }
315 }
316
317 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
318 ERR("Unable to start listening on \"%s\" (%s).", address, strerror(errno));
319 goto fail;
320 }
321
322 return sock;
323
324fail:
325 if (sock > -1) {
326 close(sock);
327 }
328
329 return -1;
330}
331
332int
Michal Vasko3031aae2016-01-27 16:07:18 +0100333nc_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 +0100334{
Michal Vaskof54cd352017-02-22 13:42:02 +0100335 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100336 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100337 struct pollfd *pfd;
338 struct sockaddr_storage saddr;
339 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100340 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100341
342 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100343 if (!pfd) {
344 ERRMEM;
345 return -1;
346 }
347
Michal Vaskoac2f6182017-01-30 14:32:03 +0100348 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200349 if (binds[i].sock < 0) {
350 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200351 continue;
352 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200353 if (binds[i].pollin) {
354 binds[i].pollin = 0;
355 /* leftover pollin */
356 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100357 break;
358 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100359 pfd[pfd_count].fd = binds[i].sock;
360 pfd[pfd_count].events = POLLIN;
361 pfd[pfd_count].revents = 0;
362
363 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100364 }
365
Michal Vasko0a3f3752016-10-13 14:58:38 +0200366 if (sock == -1) {
367 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100368 sigfillset(&sigmask);
369 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100370 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100371 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
372
Michal Vasko0a3f3752016-10-13 14:58:38 +0200373 if (!ret) {
374 /* we timeouted */
375 free(pfd);
376 return 0;
377 } else if (ret == -1) {
378 ERR("Poll failed (%s).", strerror(errno));
379 free(pfd);
380 return -1;
381 }
Michal Vasko086311b2016-01-08 09:53:11 +0100382
Michal Vaskoac2f6182017-01-30 14:32:03 +0100383 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
384 /* adjust i so that indices in binds and pfd always match */
385 while (binds[i].sock != pfd[j].fd) {
386 ++i;
387 }
388
389 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200390 --ret;
391
392 if (!ret) {
393 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100394 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200395 break;
396 } else {
397 /* just remember the event for next time */
398 binds[i].pollin = 1;
399 }
400 }
Michal Vasko086311b2016-01-08 09:53:11 +0100401 }
402 }
403 free(pfd);
404
405 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100406 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100407 return -1;
408 }
409
410 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100411 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100412 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100413 return -1;
414 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200415 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100416
Michal Vasko0190bc32016-03-02 15:47:49 +0100417 /* make the socket non-blocking */
418 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
419 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100420 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100421 return -1;
422 }
423
Michal Vasko3031aae2016-01-27 16:07:18 +0100424 if (idx) {
425 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100426 }
427
Michal Vasko086311b2016-01-08 09:53:11 +0100428 /* host was requested */
429 if (host) {
430 if (saddr.ss_family == AF_INET) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000431 *host = malloc(INET_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100432 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000433 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100434 ERR("inet_ntop failed (%s).", strerror(errno));
435 free(*host);
436 *host = NULL;
437 }
Michal Vasko086311b2016-01-08 09:53:11 +0100438
Michal Vasko4eb3c312016-03-01 14:09:37 +0100439 if (port) {
440 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
441 }
442 } else {
443 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100444 }
445 } else if (saddr.ss_family == AF_INET6) {
Jan Kundrát0f942e82018-02-14 14:52:00 +0100446 *host = malloc(INET6_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100447 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000448 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100449 ERR("inet_ntop failed (%s).", strerror(errno));
450 free(*host);
451 *host = NULL;
452 }
Michal Vasko086311b2016-01-08 09:53:11 +0100453
Michal Vasko4eb3c312016-03-01 14:09:37 +0100454 if (port) {
455 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
456 }
457 } else {
458 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100459 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200460 } else if (saddr.ss_family == AF_UNIX) {
461 *host = strdup(((struct sockaddr_un *)&saddr)->sun_path);
462 if (*host) {
463 if (port) {
464 *port = 0;
465 }
466 } else {
467 ERRMEM;
468 }
Michal Vasko086311b2016-01-08 09:53:11 +0100469 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100470 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100471 }
472 }
473
474 return ret;
475}
476
Michal Vasko05ba9df2016-01-13 14:40:27 +0100477static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100478nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100479{
480 const char *identifier = NULL, *version = NULL, *format = NULL;
481 char *model_data = NULL;
482 const struct lys_module *module;
483 struct nc_server_error *err;
484 struct lyd_node *child, *data = NULL;
Michal Vasko88639e92017-08-03 14:38:10 +0200485 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100486
487 LY_TREE_FOR(rpc->child, child) {
488 if (!strcmp(child->schema->name, "identifier")) {
489 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
490 } else if (!strcmp(child->schema->name, "version")) {
491 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500492 if (version && version[0] == '\0') {
493 version = NULL;
494 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100495 } else if (!strcmp(child->schema->name, "format")) {
496 format = ((struct lyd_node_leaf_list *)child)->value_str;
497 }
498 }
Michal Vaskoddce1212019-05-24 09:58:49 +0200499 VRB("Schema \"%s\" was requested.", identifier);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100500
501 /* check version */
502 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100503 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
504 nc_err_set_msg(err, "The requested version is not supported.", "en");
505 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100506 }
507
508 /* check and get module with the name identifier */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200509 module = ly_ctx_get_module(server_opts.ctx, identifier, version, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100510 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200511 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
512 }
513 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100514 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
515 nc_err_set_msg(err, "The requested schema was not found.", "en");
516 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100517 }
518
519 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100520 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100521 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL, 0, 0);
Radek Krejci90fba642016-12-07 15:59:45 +0100522 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100523 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100524 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100525 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
526 nc_err_set_msg(err, "The requested format is not supported.", "en");
527 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100528 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200529 if (!model_data) {
530 ERRINT;
531 return NULL;
532 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100533
Michal Vasko88639e92017-08-03 14:38:10 +0200534 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
535 if (!sdata) {
536 ERRINT;
537 free(model_data);
538 return NULL;
539 }
540
Radek Krejci539efb62016-08-24 15:05:16 +0200541 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
542 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100543 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100544 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200545 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100546 return NULL;
547 }
548
Radek Krejci36dfdb32016-09-01 16:56:35 +0200549 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100550}
551
552static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100553nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100554{
Michal Vasko428087d2016-01-14 16:04:28 +0100555 session->term_reason = NC_SESSION_TERM_CLOSED;
556 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100557}
558
Michal Vasko086311b2016-01-08 09:53:11 +0100559API int
560nc_server_init(struct ly_ctx *ctx)
561{
Michal Vasko88639e92017-08-03 14:38:10 +0200562 const struct lys_node *rpc;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000563 pthread_rwlockattr_t attr;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100564
Michal Vasko086311b2016-01-08 09:53:11 +0100565 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200566 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100567 return -1;
568 }
569
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100570 nc_init();
571
Michal Vasko05ba9df2016-01-13 14:40:27 +0100572 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200573 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
574 if (rpc && !rpc->priv) {
575 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100576 }
577
578 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200579 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
580 if (rpc && !rpc->priv) {
581 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100582 }
583
Michal Vasko086311b2016-01-08 09:53:11 +0100584 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100585
586 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500587 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100588
Frank Rimpler9f838b02018-07-25 06:44:03 +0000589 errno=0;
590
591 if (pthread_rwlockattr_init(&attr) == 0) {
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700592#if defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
Frank Rimpler9f838b02018-07-25 06:44:03 +0000593 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
594 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
595 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
596 }
597 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
598 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
599 }
600 } else {
601 ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
602 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700603#endif
Frank Rimpler9f838b02018-07-25 06:44:03 +0000604 pthread_rwlockattr_destroy(&attr);
605 } else {
606 ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
607 }
Michal Vasko086311b2016-01-08 09:53:11 +0100608 return 0;
609}
610
Michal Vaskob48aa812016-01-18 14:13:09 +0100611API void
612nc_server_destroy(void)
613{
Radek Krejci658782b2016-12-04 22:04:55 +0100614 unsigned int i;
615
616 for (i = 0; i < server_opts.capabilities_count; i++) {
617 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
618 }
619 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200620 server_opts.capabilities = NULL;
621 server_opts.capabilities_count = 0;
622
Radek Krejci53691be2016-02-22 13:58:37 +0100623#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100624 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200625 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100626#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100627#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100628 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
629 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
630 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200631 server_opts.passwd_auth_data = NULL;
632 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100633
Michal Vasko17dfda92016-12-01 14:06:16 +0100634 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100635
636 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
637 server_opts.hostkey_data_free(server_opts.hostkey_data);
638 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200639 server_opts.hostkey_data = NULL;
640 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100641#endif
642#ifdef NC_ENABLED_TLS
643 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
644 server_opts.server_cert_data_free(server_opts.server_cert_data);
645 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200646 server_opts.server_cert_data = NULL;
647 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100648 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
649 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
650 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200651 server_opts.trusted_cert_list_data = NULL;
652 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100653#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100654 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100655}
656
Michal Vasko086311b2016-01-08 09:53:11 +0100657API int
658nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
659{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200660 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
661 ERRARG("basic_mode");
662 return -1;
663 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
664 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100665 return -1;
666 }
667
668 server_opts.wd_basic_mode = basic_mode;
669 server_opts.wd_also_supported = also_supported;
670 return 0;
671}
672
Michal Vasko1a38c862016-01-15 15:50:07 +0100673API void
Michal Vasko55f03972016-04-13 08:56:01 +0200674nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
675{
676 if (!basic_mode && !also_supported) {
677 ERRARG("basic_mode and also_supported");
678 return;
679 }
680
681 if (basic_mode) {
682 *basic_mode = server_opts.wd_basic_mode;
683 }
684 if (also_supported) {
685 *also_supported = server_opts.wd_also_supported;
686 }
687}
688
Michal Vasko55f03972016-04-13 08:56:01 +0200689API int
Radek Krejci658782b2016-12-04 22:04:55 +0100690nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200691{
Radek Krejci658782b2016-12-04 22:04:55 +0100692 const char **new;
693
694 if (!value || !value[0]) {
695 ERRARG("value must not be empty");
696 return EXIT_FAILURE;
697 }
698
699 server_opts.capabilities_count++;
700 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
701 if (!new) {
702 ERRMEM;
703 return EXIT_FAILURE;
704 }
705 server_opts.capabilities = new;
706 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
707
708 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200709}
710
Michal Vasko1a38c862016-01-15 15:50:07 +0100711API void
Michal Vasko086311b2016-01-08 09:53:11 +0100712nc_server_set_hello_timeout(uint16_t hello_timeout)
713{
Michal Vasko086311b2016-01-08 09:53:11 +0100714 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100715}
716
Michal Vasko55f03972016-04-13 08:56:01 +0200717API uint16_t
718nc_server_get_hello_timeout(void)
719{
720 return server_opts.hello_timeout;
721}
722
Michal Vasko1a38c862016-01-15 15:50:07 +0100723API void
Michal Vasko086311b2016-01-08 09:53:11 +0100724nc_server_set_idle_timeout(uint16_t idle_timeout)
725{
Michal Vasko086311b2016-01-08 09:53:11 +0100726 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100727}
728
Michal Vasko55f03972016-04-13 08:56:01 +0200729API uint16_t
730nc_server_get_idle_timeout(void)
731{
732 return server_opts.idle_timeout;
733}
734
Michal Vasko71090fc2016-05-24 16:37:28 +0200735API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100736nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100737{
Michal Vasko71090fc2016-05-24 16:37:28 +0200738 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200739 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200740
Michal Vasko45e53ae2016-04-07 11:46:03 +0200741 if (!server_opts.ctx) {
742 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200743 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200744 } else if (fdin < 0) {
745 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200746 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200747 } else if (fdout < 0) {
748 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200749 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200750 } else if (!username) {
751 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200752 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200753 } else if (!session) {
754 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200755 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100756 }
757
758 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200759 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100760 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100761 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200762 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100763 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100764 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100765
Michal Vasko086311b2016-01-08 09:53:11 +0100766 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100767 (*session)->ti_type = NC_TI_FD;
768 (*session)->ti.fd.in = fdin;
769 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100770
771 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100772 (*session)->flags = NC_SESSION_SHAREDCTX;
773 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100774
Michal Vaskob48aa812016-01-18 14:13:09 +0100775 /* assign new SID atomically */
Michal Vasko7f1fa3c2020-09-08 16:30:41 +0200776 (*session)->id = ATOMIC_INC(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100777
Michal Vasko086311b2016-01-08 09:53:11 +0100778 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200779 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200780 if (msgtype != NC_MSG_HELLO) {
781 nc_session_free(*session, NULL);
782 *session = NULL;
783 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100784 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200785
786 nc_gettimespec_mono(&ts_cur);
787 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
788 nc_gettimespec_real(&ts_cur);
789 (*session)->opts.server.session_start = ts_cur.tv_sec;
790
Michal Vasko1a38c862016-01-15 15:50:07 +0100791 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100792
Michal Vasko71090fc2016-05-24 16:37:28 +0200793 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100794}
Michal Vasko9e036d52016-01-08 10:49:26 +0100795
Michal Vaskob30b99c2016-07-26 11:35:43 +0200796static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100797nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
798{
799 uint8_t q_last;
800
801 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
802 ERRINT;
803 return;
804 }
805
806 /* get a unique queue value (by adding 1 to the last added value, if any) */
807 if (ps->queue_len) {
808 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
809 *id = ps->queue[q_last] + 1;
810 } else {
811 *id = 0;
812 }
813
814 /* add the id into the queue */
815 ++ps->queue_len;
816 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
817 ps->queue[q_last] = *id;
818}
819
820static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200821nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
822{
Michal Vasko74c345f2018-02-07 10:37:11 +0100823 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200824
825 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100826 /* get the actual queue idx */
827 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200828
829 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100830 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200831 /* another equal value, simply cannot be */
832 ERRINT;
833 }
Michal Vaskod8340032018-02-12 14:41:00 +0100834 if (found == 2) {
835 /* move the following values */
836 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
837 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100838 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200839 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100840 if (i == 0) {
841 found = 1;
842 } else {
843 /* this is not okay, our id is in the middle of the queue */
844 found = 2;
845 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200846 }
847 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200848 if (!found) {
849 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100850 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200851 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100852
Michal Vasko103fe632018-02-12 16:37:45 +0100853 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100854 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100855 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100856 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
857 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200858}
859
Michal Vaskof04a52a2016-04-07 10:52:10 +0200860int
Michal Vasko26043172016-07-26 14:08:59 +0200861nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200862{
863 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200864 struct timespec ts;
865
Michal Vasko77a6abe2017-10-05 10:02:20 +0200866 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100867 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200868
869 /* LOCK */
870 ret = pthread_mutex_timedlock(&ps->lock, &ts);
871 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200872 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200873 return -1;
874 }
875
Michal Vasko74c345f2018-02-07 10:37:11 +0100876 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100877 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100878 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100879 pthread_mutex_unlock(&ps->lock);
880 return -1;
881 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100882
883 /* add ourselves into the queue */
884 nc_ps_queue_add_id(ps, id);
Michal Vasko2733aad2020-04-16 09:01:52 +0200885 DBL("PS 0x%p TID %lu queue: added %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), *id,
Michal Vasko91290952019-09-27 11:30:55 +0200886 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200887
888 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200889 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200890 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100891 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200892
893 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
894 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +0530895 /**
896 * This may happen when another thread releases the lock and broadcasts the condition
897 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
898 * but when actually this thread was ready for condition.
899 */
preetbhansali629dfc42018-12-17 16:04:40 +0530900 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +0530901 break;
902 }
Michal Vasko66032bc2019-01-22 15:03:12 +0100903
Michal Vasko26043172016-07-26 14:08:59 +0200904 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200905 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200906 nc_ps_queue_remove_id(ps, *id);
907 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200908 return -1;
909 }
910 }
911
Michal Vaskobe86fe32016-04-07 10:43:03 +0200912 /* UNLOCK */
913 pthread_mutex_unlock(&ps->lock);
914
915 return 0;
916}
917
Michal Vaskof04a52a2016-04-07 10:52:10 +0200918int
Michal Vasko26043172016-07-26 14:08:59 +0200919nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200920{
921 int ret;
922 struct timespec ts;
923
Michal Vasko77a6abe2017-10-05 10:02:20 +0200924 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100925 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200926
927 /* LOCK */
928 ret = pthread_mutex_timedlock(&ps->lock, &ts);
929 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200930 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200931 ret = -1;
932 }
933
Michal Vaskob30b99c2016-07-26 11:35:43 +0200934 /* we must be the first, it was our turn after all, right? */
935 if (ps->queue[ps->queue_begin] != id) {
936 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200937 /* UNLOCK */
938 if (!ret) {
939 pthread_mutex_unlock(&ps->lock);
940 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200941 return -1;
942 }
943
Michal Vaskobe86fe32016-04-07 10:43:03 +0200944 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200945 nc_ps_queue_remove_id(ps, id);
Michal Vasko2733aad2020-04-16 09:01:52 +0200946 DBL("PS 0x%p TID %lu queue: removed %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), id,
Michal Vasko91290952019-09-27 11:30:55 +0200947 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200948
949 /* broadcast to all other threads that the queue moved */
950 pthread_cond_broadcast(&ps->cond);
951
Michal Vaskobe86fe32016-04-07 10:43:03 +0200952 /* UNLOCK */
953 if (!ret) {
954 pthread_mutex_unlock(&ps->lock);
955 }
956
957 return ret;
958}
959
Michal Vasko428087d2016-01-14 16:04:28 +0100960API struct nc_pollsession *
961nc_ps_new(void)
962{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100963 struct nc_pollsession *ps;
964
965 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100966 if (!ps) {
967 ERRMEM;
968 return NULL;
969 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200970 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100971 pthread_mutex_init(&ps->lock, NULL);
972
973 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100974}
975
976API void
977nc_ps_free(struct nc_pollsession *ps)
978{
fanchanghu3d4e7212017-08-09 09:42:30 +0800979 uint16_t i;
980
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100981 if (!ps) {
982 return;
983 }
984
Michal Vaskobe86fe32016-04-07 10:43:03 +0200985 if (ps->queue_len) {
986 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
987 }
988
fanchanghu3d4e7212017-08-09 09:42:30 +0800989 for (i = 0; i < ps->session_count; i++) {
990 free(ps->sessions[i]);
991 }
992
Michal Vasko428087d2016-01-14 16:04:28 +0100993 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100994 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200995 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100996
Michal Vasko428087d2016-01-14 16:04:28 +0100997 free(ps);
998}
999
1000API int
1001nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1002{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001003 uint8_t q_id;
1004
Michal Vasko45e53ae2016-04-07 11:46:03 +02001005 if (!ps) {
1006 ERRARG("ps");
1007 return -1;
1008 } else if (!session) {
1009 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001010 return -1;
1011 }
1012
Michal Vasko48a63ed2016-03-01 09:48:21 +01001013 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001014 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001015 return -1;
1016 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001017
Michal Vasko428087d2016-01-14 16:04:28 +01001018 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001019 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001020 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001021 ERRMEM;
1022 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001023 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001024 return -1;
1025 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001026 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1027 if (!ps->sessions[ps->session_count - 1]) {
1028 ERRMEM;
1029 --ps->session_count;
1030 /* UNLOCK */
1031 nc_ps_unlock(ps, q_id, __func__);
1032 return -1;
1033 }
1034 ps->sessions[ps->session_count - 1]->session = session;
1035 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001036
Michal Vasko48a63ed2016-03-01 09:48:21 +01001037 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001038 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001039}
1040
Michal Vasko48a63ed2016-03-01 09:48:21 +01001041static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001042_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001043{
1044 uint16_t i;
1045
Radek Krejcid5f978f2016-03-03 13:14:45 +01001046 if (index >= 0) {
1047 i = (uint16_t)index;
1048 goto remove;
1049 }
Michal Vasko428087d2016-01-14 16:04:28 +01001050 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001051 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001052remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001053 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001054 if (i <= ps->session_count) {
1055 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001056 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001057 }
1058 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001059 free(ps->sessions);
1060 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001061 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001062 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001063 return 0;
1064 }
1065 }
1066
Michal Vaskof0537d82016-01-29 14:42:38 +01001067 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001068}
1069
Michal Vasko48a63ed2016-03-01 09:48:21 +01001070API int
1071nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1072{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001073 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001074 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001075
Michal Vasko45e53ae2016-04-07 11:46:03 +02001076 if (!ps) {
1077 ERRARG("ps");
1078 return -1;
1079 } else if (!session) {
1080 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001081 return -1;
1082 }
1083
1084 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001085 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001086 return -1;
1087 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001088
Radek Krejcid5f978f2016-03-03 13:14:45 +01001089 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001090
1091 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001092 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001093
Michal Vaskobe86fe32016-04-07 10:43:03 +02001094 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001095}
1096
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001097API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001098nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001099{
1100 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001101 struct nc_session *ret = NULL;
1102
1103 if (!ps) {
1104 ERRARG("ps");
1105 return NULL;
1106 }
1107
1108 /* LOCK */
1109 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1110 return NULL;
1111 }
1112
Michal Vasko4871c9d2017-10-09 14:48:39 +02001113 if (idx < ps->session_count) {
1114 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001115 }
1116
1117 /* UNLOCK */
1118 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1119
1120 return ret;
1121}
1122
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001123API uint16_t
1124nc_ps_session_count(struct nc_pollsession *ps)
1125{
Michal Vasko47003942019-03-14 12:25:23 +01001126 uint8_t q_id;
1127 uint16_t session_count;
1128
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001129 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001130 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001131 return 0;
1132 }
1133
Michal Vasko47003942019-03-14 12:25:23 +01001134 /* LOCK (just for memory barrier so that we read the current value) */
1135 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1136 return 0;
1137 }
1138
1139 session_count = ps->session_count;
1140
1141 /* UNLOCK */
1142 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1143
1144 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001145}
1146
Michal Vasko131120a2018-05-29 15:44:02 +02001147/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001148 * returns: NC_PSPOLL_ERROR,
1149 * NC_PSPOLL_BAD_RPC,
1150 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1151 * NC_PSPOLL_RPC
1152 */
1153static int
Michal Vasko131120a2018-05-29 15:44:02 +02001154nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001155{
1156 struct lyxml_elem *xml = NULL;
1157 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001158 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001159 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001160
Michal Vasko45e53ae2016-04-07 11:46:03 +02001161 if (!session) {
1162 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001163 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001164 } else if (!rpc) {
1165 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001166 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001167 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001168 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001169 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001170 }
1171
Michal Vasko131120a2018-05-29 15:44:02 +02001172 msgtype = nc_read_msg_io(session, io_timeout, &xml, 0);
Michal Vasko428087d2016-01-14 16:04:28 +01001173
1174 switch (msgtype) {
1175 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001176 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001177 if (!*rpc) {
1178 ERRMEM;
1179 goto error;
1180 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001181
Radek Krejcif93c7d42016-04-06 13:41:15 +02001182 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001183 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1184 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001185 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001186 /* parsing RPC failed */
Michal Vaskoc9970242018-02-14 16:03:35 +01001187 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
Michal Vasko131120a2018-05-29 15:44:02 +02001188 ret = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001189 nc_server_reply_free(reply);
Michal Vaskod057b272020-02-07 10:49:05 +01001190 if (ret != NC_MSG_REPLY) {
Michal Vasko8fe604c2020-02-10 15:25:04 +01001191 ERR("Session %u: failed to write reply (%s).", session->id, nc_msgtype2str[ret]);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001192 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001193 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1194 } else {
1195 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001196 }
Michal Vasko428087d2016-01-14 16:04:28 +01001197 (*rpc)->root = xml;
1198 break;
1199 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001200 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001201 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001202 goto error;
1203 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001204 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001205 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001206 goto error;
1207 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001208 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001209 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001210 goto error;
1211 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001212 /* NC_MSG_ERROR,
Michal Vasko131120a2018-05-29 15:44:02 +02001213 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg_io()
Michal Vasko428087d2016-01-14 16:04:28 +01001214 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001215 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001216 break;
1217 }
1218
Michal Vasko71090fc2016-05-24 16:37:28 +02001219 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001220
1221error:
1222 /* cleanup */
1223 lyxml_free(server_opts.ctx, xml);
1224
Michal Vasko71090fc2016-05-24 16:37:28 +02001225 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001226}
1227
fanchanghu966f2de2016-07-21 02:28:57 -04001228API void
1229nc_set_global_rpc_clb(nc_rpc_clb clb)
1230{
1231 global_rpc_clb = clb;
1232}
1233
Radek Krejci93e80222016-10-03 13:34:25 +02001234API NC_MSG_TYPE
1235nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1236{
Michal Vasko131120a2018-05-29 15:44:02 +02001237 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001238
1239 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001240 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001241 ERRARG("session");
1242 return NC_MSG_ERROR;
1243 } else if (!notif || !notif->tree || !notif->eventtime) {
1244 ERRARG("notif");
1245 return NC_MSG_ERROR;
1246 }
1247
Michal Vasko131120a2018-05-29 15:44:02 +02001248 /* we do not need RPC lock for this, IO lock will be acquired properly */
1249 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001250 if (ret != NC_MSG_NOTIF) {
1251 ERR("Session %u: failed to write notification (%s).", session->id, nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001252 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001253
Michal Vasko131120a2018-05-29 15:44:02 +02001254 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001255}
1256
Michal Vasko131120a2018-05-29 15:44:02 +02001257/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001258 * returns: NC_PSPOLL_ERROR,
1259 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1260 * NC_PSPOLL_REPLY_ERROR,
1261 * 0
1262 */
1263static int
Michal Vasko131120a2018-05-29 15:44:02 +02001264nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001265{
1266 nc_rpc_clb clb;
1267 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001268 struct lys_node *rpc_act = NULL;
1269 struct lyd_node *next, *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001270 int ret = 0;
1271 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001272
Michal Vasko4a827e52016-03-03 10:59:00 +01001273 if (!rpc) {
1274 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001275 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001276 }
1277
Michal Vasko90e8e692016-07-13 12:27:57 +02001278 if (rpc->tree->schema->nodetype == LYS_RPC) {
1279 /* RPC */
1280 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001281 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001282 /* action */
1283 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1284 if (elem->schema->nodetype == LYS_ACTION) {
1285 rpc_act = elem->schema;
1286 break;
1287 }
1288 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001289 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001290 if (!rpc_act) {
1291 ERRINT;
1292 return NC_PSPOLL_ERROR;
1293 }
1294 }
1295
1296 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001297 if (!global_rpc_clb) {
1298 /* no callback, reply with a not-implemented error */
1299 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
1300 } else {
1301 reply = global_rpc_clb(rpc->tree, session);
1302 }
Michal Vasko428087d2016-01-14 16:04:28 +01001303 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001304 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001305 reply = clb(rpc->tree, session);
1306 }
1307
1308 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001309 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001310 }
Michal Vasko131120a2018-05-29 15:44:02 +02001311 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->root, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001312 if (reply->type == NC_RPL_ERROR) {
1313 ret |= NC_PSPOLL_REPLY_ERROR;
1314 }
1315 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001316
Michal Vasko131120a2018-05-29 15:44:02 +02001317 if (r != NC_MSG_REPLY) {
Michal Vasko8fe604c2020-02-10 15:25:04 +01001318 ERR("Session %u: failed to write reply (%s).", session->id, nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001319 ret |= NC_PSPOLL_ERROR;
1320 }
Michal Vasko428087d2016-01-14 16:04:28 +01001321
1322 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1323 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1324 session->status = NC_STATUS_INVALID;
1325 }
1326
Michal Vasko71090fc2016-05-24 16:37:28 +02001327 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001328}
1329
Michal Vasko131120a2018-05-29 15:44:02 +02001330/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001331 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1332 * NC_PSPOLL_ERROR, (msg filled)
1333 * NC_PSPOLL_TIMEOUT,
1334 * NC_PSPOLL_RPC (some application data available),
1335 * NC_PSPOLL_SSH_CHANNEL,
1336 * NC_PSPOLL_SSH_MSG
1337 */
1338static int
Michal Vasko131120a2018-05-29 15:44:02 +02001339nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001340{
Michal Vasko9a327362017-01-11 11:31:46 +01001341 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001342 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001343#ifdef NC_ENABLED_SSH
1344 struct nc_session *new;
1345#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001346
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001347 /* check timeout first */
1348 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001349 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001350 sprintf(msg, "session idle timeout elapsed");
1351 session->status = NC_STATUS_INVALID;
1352 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1353 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1354 }
1355
Michal Vasko131120a2018-05-29 15:44:02 +02001356 r = nc_session_io_lock(session, io_timeout, __func__);
1357 if (r < 0) {
1358 sprintf(msg, "session IO lock failed to be acquired");
1359 return NC_PSPOLL_ERROR;
1360 } else if (!r) {
1361 return NC_PSPOLL_TIMEOUT;
1362 }
1363
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001364 switch (session->ti_type) {
1365#ifdef NC_ENABLED_SSH
1366 case NC_TI_LIBSSH:
1367 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001368 if (r == SSH_EOF) {
1369 sprintf(msg, "SSH channel unexpected EOF");
1370 session->status = NC_STATUS_INVALID;
1371 session->term_reason = NC_SESSION_TERM_DROPPED;
1372 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1373 } else if (r == SSH_ERROR) {
1374 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001375 session->status = NC_STATUS_INVALID;
1376 session->term_reason = NC_SESSION_TERM_OTHER;
1377 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001378 } else if (!r) {
1379 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1380 /* new SSH message */
1381 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1382 if (session->ti.libssh.next) {
1383 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1384 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1385 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1386 /* new NETCONF SSH channel */
1387 ret = NC_PSPOLL_SSH_CHANNEL;
1388 break;
1389 }
1390 }
1391 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001392 break;
1393 }
1394 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001395
Michal Vasko8dcaa882017-10-19 14:28:42 +02001396 /* just some SSH message */
1397 ret = NC_PSPOLL_SSH_MSG;
1398 } else {
1399 ret = NC_PSPOLL_TIMEOUT;
1400 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001401 } else {
1402 /* we have some application data */
1403 ret = NC_PSPOLL_RPC;
1404 }
1405 break;
1406#endif
1407#ifdef NC_ENABLED_TLS
1408 case NC_TI_OPENSSL:
1409 r = SSL_pending(session->ti.tls);
1410 if (!r) {
1411 /* no data pending in the SSL buffer, poll fd */
1412 pfd.fd = SSL_get_rfd(session->ti.tls);
1413 if (pfd.fd < 0) {
1414 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1415 ret = NC_PSPOLL_ERROR;
1416 break;
1417 }
1418 pfd.events = POLLIN;
1419 pfd.revents = 0;
1420 r = poll(&pfd, 1, 0);
1421
1422 if ((r < 0) && (errno != EINTR)) {
1423 sprintf(msg, "poll failed (%s)", strerror(errno));
1424 session->status = NC_STATUS_INVALID;
1425 ret = NC_PSPOLL_ERROR;
1426 } else if (r > 0) {
1427 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1428 sprintf(msg, "communication socket unexpectedly closed");
1429 session->status = NC_STATUS_INVALID;
1430 session->term_reason = NC_SESSION_TERM_DROPPED;
1431 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1432 } else if (pfd.revents & POLLERR) {
1433 sprintf(msg, "communication socket error");
1434 session->status = NC_STATUS_INVALID;
1435 session->term_reason = NC_SESSION_TERM_OTHER;
1436 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1437 } else {
1438 ret = NC_PSPOLL_RPC;
1439 }
1440 } else {
1441 ret = NC_PSPOLL_TIMEOUT;
1442 }
1443 } else {
1444 ret = NC_PSPOLL_RPC;
1445 }
1446 break;
1447#endif
1448 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001449 case NC_TI_UNIX:
1450 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001451 pfd.events = POLLIN;
1452 pfd.revents = 0;
1453 r = poll(&pfd, 1, 0);
1454
1455 if ((r < 0) && (errno != EINTR)) {
1456 sprintf(msg, "poll failed (%s)", strerror(errno));
1457 session->status = NC_STATUS_INVALID;
1458 ret = NC_PSPOLL_ERROR;
1459 } else if (r > 0) {
1460 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1461 sprintf(msg, "communication socket unexpectedly closed");
1462 session->status = NC_STATUS_INVALID;
1463 session->term_reason = NC_SESSION_TERM_DROPPED;
1464 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1465 } else if (pfd.revents & POLLERR) {
1466 sprintf(msg, "communication socket error");
1467 session->status = NC_STATUS_INVALID;
1468 session->term_reason = NC_SESSION_TERM_OTHER;
1469 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1470 } else {
1471 ret = NC_PSPOLL_RPC;
1472 }
1473 } else {
1474 ret = NC_PSPOLL_TIMEOUT;
1475 }
1476 break;
1477 case NC_TI_NONE:
1478 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1479 ret = NC_PSPOLL_ERROR;
1480 break;
1481 }
1482
Michal Vasko131120a2018-05-29 15:44:02 +02001483 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001484 return ret;
1485}
1486
1487API int
1488nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1489{
1490 int ret, r;
1491 uint8_t q_id;
1492 uint16_t i, j;
1493 char msg[256];
1494 struct timespec ts_timeout, ts_cur;
1495 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001496 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001497 struct nc_server_rpc *rpc = NULL;
1498
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001499 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001500 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001501 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001502 }
1503
Michal Vaskoade892d2017-02-22 13:40:35 +01001504 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001505 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001506 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001507 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001508
Michal Vaskoade892d2017-02-22 13:40:35 +01001509 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001510 nc_ps_unlock(ps, q_id, __func__);
1511 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001512 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001513
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001514 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001515 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001516 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001517 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001518 nc_addtimespec(&ts_timeout, timeout);
1519 }
1520
1521 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001522 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001523 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001524 if (ps->last_event_session == ps->session_count - 1) {
1525 i = j = 0;
1526 } else {
1527 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001528 }
Michal Vasko9a327362017-01-11 11:31:46 +01001529 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001530 cur_ps_session = ps->sessions[i];
1531 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001532
Michal Vasko131120a2018-05-29 15:44:02 +02001533 /* SESSION RPC LOCK */
1534 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001535 if (r == -1) {
1536 ret = NC_PSPOLL_ERROR;
1537 } else if (r == 1) {
1538 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001539 switch (cur_ps_session->state) {
1540 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001541 if (cur_session->status == NC_STATUS_RUNNING) {
1542 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001543 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001544
Michal Vasko131120a2018-05-29 15:44:02 +02001545 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001546 switch (ret) {
1547 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1548 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001549 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001550 break;
1551 case NC_PSPOLL_ERROR:
1552 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001553 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001554 break;
1555 case NC_PSPOLL_TIMEOUT:
1556#ifdef NC_ENABLED_SSH
1557 case NC_PSPOLL_SSH_CHANNEL:
1558 case NC_PSPOLL_SSH_MSG:
1559#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001560 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001561 break;
1562 case NC_PSPOLL_RPC:
1563 /* let's keep the state busy, we are not done with this session */
1564 break;
1565 }
1566 } else {
1567 /* session is not fine, let the caller know */
1568 ret = NC_PSPOLL_SESSION_TERM;
1569 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1570 ret |= NC_PSPOLL_SESSION_ERROR;
1571 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001572 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001573 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001574 break;
1575 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001576 /* it definitely should not be busy because we have the lock */
1577 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001578 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001579 break;
1580 case NC_PS_STATE_INVALID:
1581 /* we got it locked, but it will be freed, let it be */
1582 ret = NC_PSPOLL_TIMEOUT;
1583 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001584 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001585
Michal Vasko131120a2018-05-29 15:44:02 +02001586 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001587 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001588 /* SESSION RPC UNLOCK */
1589 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001590 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001591 } else {
1592 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001593 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001594 }
Michal Vasko428087d2016-01-14 16:04:28 +01001595
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001596 /* something happened */
1597 if (ret != NC_PSPOLL_TIMEOUT) {
1598 break;
1599 }
1600
Michal Vasko9a327362017-01-11 11:31:46 +01001601 if (i == ps->session_count - 1) {
1602 i = 0;
1603 } else {
1604 ++i;
1605 }
1606 } while (i != j);
1607
Michal Vaskoade892d2017-02-22 13:40:35 +01001608 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001609 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001610 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001611 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001612 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001613
1614 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1615 /* final timeout */
1616 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001617 }
Michal Vasko428087d2016-01-14 16:04:28 +01001618 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001619 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001620
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001621 /* do we want to return the session? */
1622 switch (ret) {
1623 case NC_PSPOLL_RPC:
1624 case NC_PSPOLL_SESSION_TERM:
1625 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1626#ifdef NC_ENABLED_SSH
1627 case NC_PSPOLL_SSH_CHANNEL:
1628 case NC_PSPOLL_SSH_MSG:
1629#endif
1630 if (session) {
1631 *session = cur_session;
1632 }
1633 ps->last_event_session = i;
1634 break;
1635 default:
1636 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001637 }
Michal Vasko428087d2016-01-14 16:04:28 +01001638
Michal Vaskoade892d2017-02-22 13:40:35 +01001639 /* PS UNLOCK */
1640 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001641
Michal Vasko131120a2018-05-29 15:44:02 +02001642 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001643 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001644 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001645 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1646 if (cur_session->status != NC_STATUS_RUNNING) {
1647 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001648 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001649 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001650 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001651 }
1652 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001653 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001654
Michal Vasko7f1ee932018-10-11 09:41:42 +02001655 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001656 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001657 if (cur_session->status != NC_STATUS_RUNNING) {
1658 ret |= NC_PSPOLL_SESSION_TERM;
1659 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1660 ret |= NC_PSPOLL_SESSION_ERROR;
1661 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001662 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001663 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001664 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001665 }
Michal Vasko428087d2016-01-14 16:04:28 +01001666 }
Michal Vasko7f1ee932018-10-11 09:41:42 +02001667 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001668
Michal Vasko131120a2018-05-29 15:44:02 +02001669 /* SESSION RPC UNLOCK */
1670 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001671 }
1672
Michal Vasko48a63ed2016-03-01 09:48:21 +01001673 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001674}
1675
Michal Vaskod09eae62016-02-01 10:32:52 +01001676API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001677nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001678{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001679 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001680 uint16_t i;
1681 struct nc_session *session;
1682
Michal Vasko9a25e932016-02-01 10:36:42 +01001683 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001684 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001685 return;
1686 }
1687
Michal Vasko48a63ed2016-03-01 09:48:21 +01001688 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001689 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001690 return;
1691 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001692
Michal Vasko48a63ed2016-03-01 09:48:21 +01001693 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001694 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001695 nc_session_free(ps->sessions[i]->session, data_free);
1696 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001697 }
1698 free(ps->sessions);
1699 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001700 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001701 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001702 } else {
1703 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001704 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1705 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001706 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001707 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001708 continue;
1709 }
1710
1711 ++i;
1712 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001713 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001714
1715 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001716 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001717}
1718
Michal Vasko5f352c52019-07-10 16:12:06 +02001719static int
apropp-molex4e903c32020-04-20 03:06:58 -04001720nc_get_uid(int sock, uid_t *uid)
1721{
Michal Vaskod3910912020-04-20 09:12:49 +02001722 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04001723
Michal Vaskod3910912020-04-20 09:12:49 +02001724#ifdef SO_PEERCRED
1725 struct ucred ucred;
1726 socklen_t len;
1727 len = sizeof(ucred);
1728 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
1729 if (!ret) {
1730 *uid = ucred.uid;
1731 }
1732#else
1733 ret = getpeereid(sock, uid, NULL);
1734#endif
1735
1736 if (ret < 0) {
1737 ERR("Failed to get credentials from unix socket (%s).", strerror(errno));
1738 return -1;
1739 }
apropp-molex4e903c32020-04-20 03:06:58 -04001740 return 0;
1741}
1742
1743static int
Michal Vasko5f352c52019-07-10 16:12:06 +02001744nc_accept_unix(struct nc_session *session, int sock)
1745{
apropp-molex4e903c32020-04-20 03:06:58 -04001746#if defined(SO_PEERCRED) || defined(HAVE_GETPEEREID)
Michal Vasko5f352c52019-07-10 16:12:06 +02001747 const struct passwd *pw;
Michal Vasko5f352c52019-07-10 16:12:06 +02001748 char *username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001749 session->ti_type = NC_TI_UNIX;
apropp-molex4e903c32020-04-20 03:06:58 -04001750 uid_t uid;
Michal Vasko5f352c52019-07-10 16:12:06 +02001751
Michal Vaskod3910912020-04-20 09:12:49 +02001752 if (nc_get_uid(sock, &uid)) {
1753 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02001754 return -1;
1755 }
1756
apropp-molex4e903c32020-04-20 03:06:58 -04001757 pw = getpwuid(uid);
Michal Vasko5f352c52019-07-10 16:12:06 +02001758 if (pw == NULL) {
apropp-molex4e903c32020-04-20 03:06:58 -04001759 ERR("Failed to find username for uid=%u (%s).\n", uid,
Michal Vasko5f352c52019-07-10 16:12:06 +02001760 strerror(errno));
1761 close(sock);
1762 return -1;
1763 }
1764
1765 username = strdup(pw->pw_name);
1766 if (username == NULL) {
1767 ERRMEM;
1768 close(sock);
1769 return -1;
1770 }
1771 session->username = lydict_insert_zc(server_opts.ctx, username);
1772
1773 session->ti.unixsock.sock = sock;
1774
1775 return 1;
Claus Klein22091912020-01-20 13:45:47 +01001776#else
1777 return -1;
1778#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02001779}
1780
Michal Vaskoe2713da2016-08-22 16:06:40 +02001781API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001782nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001783{
Michal Vasko3031aae2016-01-27 16:07:18 +01001784 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001785 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001786
Michal Vasko45e53ae2016-04-07 11:46:03 +02001787 if (!name) {
1788 ERRARG("name");
1789 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001790 }
1791
Michal Vaskoade892d2017-02-22 13:40:35 +01001792 /* BIND LOCK */
1793 pthread_mutex_lock(&server_opts.bind_lock);
1794
1795 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001796 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001797
1798 /* check name uniqueness */
1799 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001800 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001801 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001802 ret = -1;
1803 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001804 }
1805 }
1806
Michal Vasko3031aae2016-01-27 16:07:18 +01001807 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001808 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001809 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001810 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001811 ret = -1;
1812 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001813 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001814 memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001815 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001816 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001817 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1818 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1819 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001820
Michal Vaskoe2713da2016-08-22 16:06:40 +02001821 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001822 if (!server_opts.binds) {
1823 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001824 ret = -1;
1825 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001826 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001827
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001828 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001829 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1830
1831 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001832#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001833 case NC_TI_LIBSSH:
1834 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1835 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1836 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001837 ret = -1;
1838 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001839 }
1840 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1841 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1842 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02001843 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001844 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001845#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001846#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001847 case NC_TI_OPENSSL:
1848 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1849 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1850 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001851 ret = -1;
1852 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001853 }
1854 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001855#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001856 case NC_TI_UNIX:
1857 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1858 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1859 ERRMEM;
1860 ret = -1;
1861 goto cleanup;
1862 }
1863 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1864 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1865 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1866 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001867 default:
1868 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001869 ret = -1;
1870 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001871 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001872
Michal Vaskoade892d2017-02-22 13:40:35 +01001873cleanup:
1874 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001875 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001876
Michal Vaskoade892d2017-02-22 13:40:35 +01001877 /* BIND UNLOCK */
1878 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001879
Michal Vaskoade892d2017-02-22 13:40:35 +01001880 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001881}
1882
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001883API int
Michal Vasko59050372016-11-22 14:33:55 +01001884nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001885{
1886 uint32_t i;
1887 int ret = -1;
1888
Michal Vaskoade892d2017-02-22 13:40:35 +01001889 /* BIND LOCK */
1890 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001891
Michal Vaskoade892d2017-02-22 13:40:35 +01001892 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001893 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001894
Michal Vasko59050372016-11-22 14:33:55 +01001895 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001896 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001897 for (i = 0; i < server_opts.endpt_count; ++i) {
1898 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001899 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001900#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001901 case NC_TI_LIBSSH:
1902 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1903 free(server_opts.endpts[i].opts.ssh);
1904 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001905#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001906#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001907 case NC_TI_OPENSSL:
1908 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1909 free(server_opts.endpts[i].opts.tls);
1910 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001911#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001912 case NC_TI_UNIX:
1913 free(server_opts.endpts[i].opts.unixsock);
1914 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001915 default:
1916 ERRINT;
1917 /* won't get here ...*/
1918 break;
1919 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001920 ret = 0;
1921 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001922 free(server_opts.endpts);
1923 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001924
1925 /* remove all binds */
1926 for (i = 0; i < server_opts.endpt_count; ++i) {
1927 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1928 if (server_opts.binds[i].sock > -1) {
1929 close(server_opts.binds[i].sock);
1930 }
1931 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001932 free(server_opts.binds);
1933 server_opts.binds = NULL;
1934
Michal Vasko3031aae2016-01-27 16:07:18 +01001935 server_opts.endpt_count = 0;
1936
Michal Vasko1a38c862016-01-15 15:50:07 +01001937 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001938 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001939 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001940 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001941 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001942 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001943 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001944#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001945 case NC_TI_LIBSSH:
1946 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1947 free(server_opts.endpts[i].opts.ssh);
1948 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001949#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001950#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001951 case NC_TI_OPENSSL:
1952 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1953 free(server_opts.endpts[i].opts.tls);
1954 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001955#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001956 case NC_TI_UNIX:
1957 free(server_opts.endpts[i].opts.unixsock);
1958 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001959 default:
1960 ERRINT;
1961 break;
1962 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001963
Michal Vaskoe2713da2016-08-22 16:06:40 +02001964 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001965 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1966 if (server_opts.binds[i].sock > -1) {
1967 close(server_opts.binds[i].sock);
1968 }
1969
1970 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001971 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001972 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001973 free(server_opts.binds);
1974 server_opts.binds = NULL;
1975 free(server_opts.endpts);
1976 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001977 } else if (i < server_opts.endpt_count) {
1978 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1979 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001980 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001981
1982 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001983 if (name) {
1984 break;
1985 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001986 }
1987 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001988 }
1989
Michal Vaskoade892d2017-02-22 13:40:35 +01001990 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001991 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001992
Michal Vaskoade892d2017-02-22 13:40:35 +01001993 /* BIND UNLOCK */
1994 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001995
1996 return ret;
1997}
1998
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001999API int
2000nc_server_endpt_count(void)
2001{
2002 return server_opts.endpt_count;
2003}
2004
Michal Vasko1b5973e2020-01-30 16:05:46 +01002005API int
2006nc_server_is_endpt(const char *name)
2007{
2008 uint16_t i;
2009 int found = 0;
2010
Michal Vaskofb1724b2020-01-31 11:02:00 +01002011 if (!name) {
2012 return found;
2013 }
2014
Michal Vasko1b5973e2020-01-30 16:05:46 +01002015 /* ENDPT READ LOCK */
2016 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2017
2018 /* check name uniqueness */
2019 for (i = 0; i < server_opts.endpt_count; ++i) {
2020 if (!strcmp(server_opts.endpts[i].name, name)) {
2021 found = 1;
2022 break;
2023 }
2024 }
2025
2026 /* ENDPT UNLOCK */
2027 pthread_rwlock_unlock(&server_opts.endpt_lock);
2028
2029 return found;
2030}
2031
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002032int
2033nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2034{
2035 struct nc_endpt *endpt;
2036 struct nc_bind *bind = NULL;
2037 uint16_t i;
2038 int sock = -1, set_addr, ret = 0;
2039
2040 if (!endpt_name) {
2041 ERRARG("endpt_name");
2042 return -1;
2043 } else if ((!address && !port) || (address && port)) {
2044 ERRARG("address and port");
2045 return -1;
2046 }
2047
2048 if (address) {
2049 set_addr = 1;
2050 } else {
2051 set_addr = 0;
2052 }
2053
2054 /* BIND LOCK */
2055 pthread_mutex_lock(&server_opts.bind_lock);
2056
2057 /* ENDPT LOCK */
2058 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2059 if (!endpt) {
2060 /* BIND UNLOCK */
2061 pthread_mutex_unlock(&server_opts.bind_lock);
2062 return -1;
2063 }
2064
2065 bind = &server_opts.binds[i];
2066
2067 if (set_addr) {
2068 port = bind->port;
2069 } else {
2070 address = bind->address;
2071 }
2072
2073 if (!set_addr && endpt->ti == NC_TI_UNIX) {
2074 ret = -1;
2075 goto cleanup;
2076 }
2077
2078 /* we have all the information we need to create a listening socket */
2079 if (address && (port || endpt->ti == NC_TI_UNIX)) {
2080 /* create new socket, close the old one */
2081 if (endpt->ti == NC_TI_UNIX)
2082 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
2083 else
2084 sock = nc_sock_listen_inet(address, port, &endpt->ka);
2085 if (sock == -1) {
2086 ret = -1;
2087 goto cleanup;
2088 }
2089
2090 if (bind->sock > -1) {
2091 close(bind->sock);
2092 }
2093 bind->sock = sock;
2094 } /* else we are just setting address or port */
2095
2096 if (set_addr) {
2097 lydict_remove(server_opts.ctx, bind->address);
2098 bind->address = lydict_insert(server_opts.ctx, address, 0);
2099 } else {
2100 bind->port = port;
2101 }
2102
2103 if (sock > -1) {
Michal Vasko946cacb2020-08-12 11:18:08 +02002104 switch (endpt->ti) {
2105 case NC_TI_UNIX:
2106 VRB("Listening on %s for UNIX connections.", address);
2107 break;
2108#ifdef NC_ENABLED_SSH
2109 case NC_TI_LIBSSH:
2110 VRB("Listening on %s:%u for SSH connections.", address, port);
2111 break;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002112#endif
Michal Vasko946cacb2020-08-12 11:18:08 +02002113#ifdef NC_ENABLED_TLS
2114 case NC_TI_OPENSSL:
2115 VRB("Listening on %s:%u for TLS connections.", address, port);
2116 break;
2117#endif
2118 default:
2119 ERRINT;
2120 break;
2121 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002122 }
2123
2124cleanup:
2125 /* ENDPT UNLOCK */
2126 pthread_rwlock_unlock(&server_opts.endpt_lock);
2127
2128 /* BIND UNLOCK */
2129 pthread_mutex_unlock(&server_opts.bind_lock);
2130
2131 return ret;
2132}
2133
2134API int
2135nc_server_endpt_set_address(const char *endpt_name, const char *address)
2136{
2137 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2138}
2139
Michal Vasko946cacb2020-08-12 11:18:08 +02002140#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
2141
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002142API int
2143nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2144{
2145 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2146}
2147
Michal Vasko946cacb2020-08-12 11:18:08 +02002148#endif
2149
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002150API int
2151nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2152{
2153 struct nc_endpt *endpt;
2154 uint16_t i;
2155 int ret = 0;
2156
2157 if (!endpt_name) {
2158 ERRARG("endpt_name");
2159 return -1;
2160 } else if (mode == 0) {
2161 ERRARG("mode");
2162 return -1;
2163 }
2164
2165 /* ENDPT LOCK */
2166 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2167 if (!endpt)
2168 return -1;
2169
2170 if (endpt->ti != NC_TI_UNIX) {
2171 ret = -1;
2172 goto cleanup;
2173 }
2174
2175 endpt->opts.unixsock->mode = mode;
2176 endpt->opts.unixsock->uid = uid;
2177 endpt->opts.unixsock->gid = gid;
2178
2179cleanup:
2180 /* ENDPT UNLOCK */
2181 pthread_rwlock_unlock(&server_opts.endpt_lock);
2182
2183 return ret;
2184}
2185
2186API int
2187nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2188{
2189 struct nc_endpt *endpt;
2190 int ret = 0;
2191
2192 if (!endpt_name) {
2193 ERRARG("endpt_name");
2194 return -1;
2195 }
2196
2197 /* ENDPT LOCK */
2198 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2199 if (!endpt) {
2200 return -1;
2201 }
2202
2203 endpt->ka.enabled = (enable ? 1 : 0);
2204
2205 /* ENDPT UNLOCK */
2206 pthread_rwlock_unlock(&server_opts.endpt_lock);
2207
2208 return ret;
2209}
2210
2211API int
2212nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2213{
2214 struct nc_endpt *endpt;
2215 int ret = 0;
2216
2217 if (!endpt_name) {
2218 ERRARG("endpt_name");
2219 return -1;
2220 }
2221
2222 /* ENDPT LOCK */
2223 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2224 if (!endpt) {
2225 return -1;
2226 }
2227
2228 if (idle_time > -1) {
2229 endpt->ka.idle_time = idle_time;
2230 }
2231 if (max_probes > -1) {
2232 endpt->ka.max_probes = max_probes;
2233 }
2234 if (probe_interval > -1) {
2235 endpt->ka.probe_interval = probe_interval;
2236 }
2237
2238 /* ENDPT UNLOCK */
2239 pthread_rwlock_unlock(&server_opts.endpt_lock);
2240
2241 return ret;
2242}
2243
Michal Vasko71090fc2016-05-24 16:37:28 +02002244API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002245nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002246{
Michal Vasko71090fc2016-05-24 16:37:28 +02002247 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002248 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002249 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002250 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002251 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002252
Michal Vasko45e53ae2016-04-07 11:46:03 +02002253 if (!server_opts.ctx) {
2254 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002255 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002256 } else if (!session) {
2257 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002258 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002259 }
2260
Michal Vaskoade892d2017-02-22 13:40:35 +01002261 /* BIND LOCK */
2262 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002263
2264 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002265 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002266 /* BIND UNLOCK */
2267 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002268 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002269 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002270
Michal Vaskoe2713da2016-08-22 16:06:40 +02002271 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002272 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002273 /* BIND UNLOCK */
2274 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002275 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002276 if (!ret) {
2277 return NC_MSG_WOULDBLOCK;
2278 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002279 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002280 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002281
2282 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2283 /* ENDPT READ LOCK */
2284 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2285
2286 /* BIND UNLOCK */
2287 pthread_mutex_unlock(&server_opts.bind_lock);
2288
Michal Vaskob48aa812016-01-18 14:13:09 +01002289 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002290
Michal Vasko131120a2018-05-29 15:44:02 +02002291 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002292 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002293 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002294 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002295 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002296 msgtype = NC_MSG_ERROR;
2297 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002298 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002299 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002300 (*session)->ctx = server_opts.ctx;
2301 (*session)->flags = NC_SESSION_SHAREDCTX;
2302 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
2303 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002304
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002305 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002306#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002307 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2308 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002309 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002310 if (ret < 0) {
2311 msgtype = NC_MSG_ERROR;
2312 goto cleanup;
2313 } else if (!ret) {
2314 msgtype = NC_MSG_WOULDBLOCK;
2315 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002316 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002317 } else
2318#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002319#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002320 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2321 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002322 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002323 if (ret < 0) {
2324 msgtype = NC_MSG_ERROR;
2325 goto cleanup;
2326 } else if (!ret) {
2327 msgtype = NC_MSG_WOULDBLOCK;
2328 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002329 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002330 } else
2331#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002332 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2333 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2334 ret = nc_accept_unix(*session, sock);
2335 if (ret < 0) {
2336 msgtype = NC_MSG_ERROR;
2337 goto cleanup;
2338 }
2339 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002340 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002341 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002342 msgtype = NC_MSG_ERROR;
2343 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002344 }
2345
Michal Vasko2cc4c682016-03-01 09:16:48 +01002346 (*session)->data = NULL;
2347
Michal Vaskoade892d2017-02-22 13:40:35 +01002348 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002349 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002350
Michal Vaskob48aa812016-01-18 14:13:09 +01002351 /* assign new SID atomically */
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002352 (*session)->id = ATOMIC_INC(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002353
Michal Vasko9e036d52016-01-08 10:49:26 +01002354 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002355 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002356 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002357 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002358 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002359 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002360 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002361
2362 nc_gettimespec_mono(&ts_cur);
2363 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2364 nc_gettimespec_real(&ts_cur);
2365 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002366 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002367
Michal Vasko71090fc2016-05-24 16:37:28 +02002368 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002369
Michal Vasko71090fc2016-05-24 16:37:28 +02002370cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002371 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002372 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002373
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002374 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002375 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002376 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002377}
2378
Michal Vasko946cacb2020-08-12 11:18:08 +02002379#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
2380
Michal Vaskoadf30f02019-06-24 09:34:47 +02002381/* client is expected to be locked */
2382static int
2383_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 +02002384{
2385 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002386 int ret = -1;
2387
2388 if (!endpt_name) {
2389 /* remove all endpoints */
2390 for (i = 0; i < client->ch_endpt_count; ++i) {
2391 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2392 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2393 if (client->ch_endpts[i].sock_pending != -1) {
2394 close(client->ch_endpts[i].sock_pending);
2395 }
2396 switch (client->ch_endpts[i].ti) {
2397#ifdef NC_ENABLED_SSH
2398 case NC_TI_LIBSSH:
2399 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2400 free(client->ch_endpts[i].opts.ssh);
2401 break;
2402#endif
2403#ifdef NC_ENABLED_TLS
2404 case NC_TI_OPENSSL:
2405 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2406 free(client->ch_endpts[i].opts.tls);
2407 break;
2408#endif
2409 default:
2410 ERRINT;
2411 /* won't get here ...*/
2412 break;
2413 }
2414 }
2415 free(client->ch_endpts);
2416 client->ch_endpts = NULL;
2417 client->ch_endpt_count = 0;
2418
2419 ret = 0;
2420 } else {
2421 for (i = 0; i < client->ch_endpt_count; ++i) {
2422 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
2423 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2424 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2425 if (client->ch_endpts[i].sock_pending != -1) {
2426 close(client->ch_endpts[i].sock_pending);
2427 }
2428 switch (client->ch_endpts[i].ti) {
2429#ifdef NC_ENABLED_SSH
2430 case NC_TI_LIBSSH:
2431 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2432 free(client->ch_endpts[i].opts.ssh);
2433 break;
2434#endif
2435#ifdef NC_ENABLED_TLS
2436 case NC_TI_OPENSSL:
2437 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2438 free(client->ch_endpts[i].opts.tls);
2439 break;
2440#endif
2441 default:
2442 ERRINT;
2443 /* won't get here ...*/
2444 break;
2445 }
2446
2447 /* move last endpoint to the empty space */
2448 --client->ch_endpt_count;
2449 if (i < client->ch_endpt_count) {
2450 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2451 } else if (!server_opts.ch_client_count) {
2452 free(server_opts.ch_clients);
2453 server_opts.ch_clients = NULL;
2454 }
2455
2456 ret = 0;
2457 break;
2458 }
2459 }
2460 }
2461
2462 return ret;
2463}
2464
2465API int
2466nc_server_ch_add_client(const char *name)
2467{
2468 uint16_t i;
2469 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002470
2471 if (!name) {
2472 ERRARG("name");
2473 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002474 }
2475
2476 /* WRITE LOCK */
2477 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2478
2479 /* check name uniqueness */
2480 for (i = 0; i < server_opts.ch_client_count; ++i) {
2481 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2482 ERR("Call Home client \"%s\" already exists.", name);
2483 /* WRITE UNLOCK */
2484 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2485 return -1;
2486 }
2487 }
2488
2489 ++server_opts.ch_client_count;
2490 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2491 if (!server_opts.ch_clients) {
2492 ERRMEM;
2493 /* WRITE UNLOCK */
2494 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2495 return -1;
2496 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002497 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002498
Michal Vaskoadf30f02019-06-24 09:34:47 +02002499 client->name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002500 client->id = ATOMIC_INC(server_opts.new_client_id);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002501 client->ch_endpts = NULL;
2502 client->ch_endpt_count = 0;
2503 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002504
Michal Vasko2e6defd2016-10-07 15:48:15 +02002505 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002506 client->start_with = NC_CH_FIRST_LISTED;
2507 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002508
Michal Vaskoadf30f02019-06-24 09:34:47 +02002509 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002510
2511 /* WRITE UNLOCK */
2512 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2513
2514 return 0;
2515}
2516
2517API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002518nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002519{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002520 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002521 int ret = -1;
2522
2523 /* WRITE LOCK */
2524 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2525
Michal Vaskoadf30f02019-06-24 09:34:47 +02002526 if (!name) {
2527 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002528 for (i = 0; i < server_opts.ch_client_count; ++i) {
2529 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2530
2531 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002532 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002533
2534 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002535 ret = 0;
2536 }
2537 free(server_opts.ch_clients);
2538 server_opts.ch_clients = NULL;
2539
2540 server_opts.ch_client_count = 0;
2541
2542 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002543 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002544 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002545 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002546 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2547
Michal Vasko2e6defd2016-10-07 15:48:15 +02002548 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002549 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002550
2551 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2552
2553 /* move last client and endpoint(s) to the empty space */
2554 --server_opts.ch_client_count;
2555 if (i < server_opts.ch_client_count) {
2556 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002557 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002558 } else if (!server_opts.ch_client_count) {
2559 free(server_opts.ch_clients);
2560 server_opts.ch_clients = NULL;
2561 }
2562
2563 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002564 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002565 }
2566 }
2567 }
2568
2569 /* WRITE UNLOCK */
2570 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2571
2572 return ret;
2573}
2574
2575API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002576nc_server_ch_is_client(const char *name)
2577{
2578 uint16_t i;
2579 int found = 0;
2580
2581 if (!name) {
2582 return found;
2583 }
2584
2585 /* READ LOCK */
2586 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2587
2588 /* check name uniqueness */
2589 for (i = 0; i < server_opts.ch_client_count; ++i) {
2590 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2591 found = 1;
2592 break;
2593 }
2594 }
2595
2596 /* UNLOCK */
2597 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2598
2599 return found;
2600}
2601
2602API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002603nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002604{
2605 uint16_t i;
2606 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002607 struct nc_ch_endpt *endpt;
2608 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002609
2610 if (!client_name) {
2611 ERRARG("client_name");
2612 return -1;
2613 } else if (!endpt_name) {
2614 ERRARG("endpt_name");
2615 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002616 } else if (!ti) {
2617 ERRARG("ti");
2618 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002619 }
2620
2621 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002622 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002623 if (!client) {
2624 return -1;
2625 }
2626
2627 for (i = 0; i < client->ch_endpt_count; ++i) {
2628 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2629 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002630 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002631 }
2632 }
2633
2634 ++client->ch_endpt_count;
2635 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2636 if (!client->ch_endpts) {
2637 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002638 goto cleanup;
2639 }
2640 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2641
2642 memset(endpt, 0, sizeof *client->ch_endpts);
2643 endpt->name = lydict_insert(server_opts.ctx, endpt_name, 0);
2644 endpt->ti = ti;
2645 endpt->sock_pending = -1;
2646 endpt->ka.idle_time = 1;
2647 endpt->ka.max_probes = 10;
2648 endpt->ka.probe_interval = 5;
2649
2650 switch (ti) {
2651#ifdef NC_ENABLED_SSH
2652 case NC_TI_LIBSSH:
2653 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2654 if (!endpt->opts.ssh) {
2655 ERRMEM;
2656 goto cleanup;
2657 }
2658 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2659 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002660 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002661 break;
2662#endif
2663#ifdef NC_ENABLED_TLS
2664 case NC_TI_OPENSSL:
2665 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2666 if (!endpt->opts.tls) {
2667 ERRMEM;
2668 goto cleanup;
2669 }
2670 break;
2671#endif
2672 default:
2673 ERRINT;
2674 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002675 }
2676
Michal Vaskoadf30f02019-06-24 09:34:47 +02002677 /* success */
2678 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002679
Michal Vaskoadf30f02019-06-24 09:34:47 +02002680cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002681 /* UNLOCK */
2682 nc_server_ch_client_unlock(client);
2683
Michal Vaskoadf30f02019-06-24 09:34:47 +02002684 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002685}
2686
2687API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002688nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002689{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002690 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002691 struct nc_ch_client *client;
2692
2693 if (!client_name) {
2694 ERRARG("client_name");
2695 return -1;
2696 }
2697
2698 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002699 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002700 if (!client) {
2701 return -1;
2702 }
2703
Michal Vaskoadf30f02019-06-24 09:34:47 +02002704 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002705
2706 /* UNLOCK */
2707 nc_server_ch_client_unlock(client);
2708
2709 return ret;
2710}
2711
2712API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002713nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2714{
2715 uint16_t i;
2716 struct nc_ch_client *client = NULL;
2717 int found = 0;
2718
2719 if (!client_name || !endpt_name) {
2720 return found;
2721 }
2722
2723 /* READ LOCK */
2724 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2725
2726 for (i = 0; i < server_opts.ch_client_count; ++i) {
2727 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2728 client = &server_opts.ch_clients[i];
2729 break;
2730 }
2731 }
2732
2733 if (!client) {
2734 goto cleanup;
2735 }
2736
2737 for (i = 0; i < client->ch_endpt_count; ++i) {
2738 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2739 found = 1;
2740 break;
2741 }
2742 }
2743
2744cleanup:
2745 /* UNLOCK */
2746 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2747 return found;
2748}
2749
2750API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002751nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2752{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002753 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002754 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002755
2756 if (!client_name) {
2757 ERRARG("client_name");
2758 return -1;
2759 } else if (!endpt_name) {
2760 ERRARG("endpt_name");
2761 return -1;
2762 } else if (!address) {
2763 ERRARG("address");
2764 return -1;
2765 }
2766
2767 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002768 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2769 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002770 return -1;
2771 }
2772
Michal Vaskoadf30f02019-06-24 09:34:47 +02002773 lydict_remove(server_opts.ctx, endpt->address);
2774 endpt->address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002775
2776 /* UNLOCK */
2777 nc_server_ch_client_unlock(client);
2778
Michal Vaskoadf30f02019-06-24 09:34:47 +02002779 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002780}
2781
2782API int
2783nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2784{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002785 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002786 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002787
2788 if (!client_name) {
2789 ERRARG("client_name");
2790 return -1;
2791 } else if (!endpt_name) {
2792 ERRARG("endpt_name");
2793 return -1;
2794 } else if (!port) {
2795 ERRARG("port");
2796 return -1;
2797 }
2798
2799 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002800 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2801 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002802 return -1;
2803 }
2804
Michal Vaskoadf30f02019-06-24 09:34:47 +02002805 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002806
2807 /* UNLOCK */
2808 nc_server_ch_client_unlock(client);
2809
ravsz5c5a4422020-03-31 15:53:21 +02002810 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002811}
2812
2813API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002814nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2815{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002816 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002817 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002818
2819 if (!client_name) {
2820 ERRARG("client_name");
2821 return -1;
2822 } else if (!endpt_name) {
2823 ERRARG("endpt_name");
2824 return -1;
2825 }
2826
2827 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002828 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2829 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002830 return -1;
2831 }
2832
Michal Vaskoadf30f02019-06-24 09:34:47 +02002833 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002834
2835 /* UNLOCK */
2836 nc_server_ch_client_unlock(client);
2837
Michal Vasko9af829a2019-09-12 13:50:00 +02002838 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002839}
2840
2841API int
2842nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
2843 int probe_interval)
2844{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002845 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002846 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002847
2848 if (!client_name) {
2849 ERRARG("client_name");
2850 return -1;
2851 } else if (!endpt_name) {
2852 ERRARG("endpt_name");
2853 return -1;
2854 }
2855
2856 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002857 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2858 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002859 return -1;
2860 }
2861
Michal Vaskoadf30f02019-06-24 09:34:47 +02002862 if (idle_time > -1) {
2863 endpt->ka.idle_time = idle_time;
2864 }
2865 if (max_probes > -1) {
2866 endpt->ka.max_probes = max_probes;
2867 }
2868 if (probe_interval > -1) {
2869 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002870 }
2871
2872 /* UNLOCK */
2873 nc_server_ch_client_unlock(client);
2874
Michal Vasko9af829a2019-09-12 13:50:00 +02002875 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002876}
2877
2878API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002879nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2880{
2881 struct nc_ch_client *client;
2882
2883 if (!client_name) {
2884 ERRARG("client_name");
2885 return -1;
2886 } else if (!conn_type) {
2887 ERRARG("conn_type");
2888 return -1;
2889 }
2890
2891 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002892 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002893 if (!client) {
2894 return -1;
2895 }
2896
2897 if (client->conn_type != conn_type) {
2898 client->conn_type = conn_type;
2899
2900 /* set default options */
2901 switch (conn_type) {
2902 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002903 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002904 break;
2905 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002906 client->conn.period.period = 60;
2907 client->conn.period.anchor_time = 0;
2908 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002909 break;
2910 default:
2911 ERRINT;
2912 break;
2913 }
2914 }
2915
2916 /* UNLOCK */
2917 nc_server_ch_client_unlock(client);
2918
2919 return 0;
2920}
2921
2922API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002923nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
2924{
2925 struct nc_ch_client *client;
2926
2927 if (!client_name) {
2928 ERRARG("client_name");
2929 return -1;
2930 } else if (!period) {
2931 ERRARG("period");
2932 return -1;
2933 }
2934
2935 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002936 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002937 if (!client) {
2938 return -1;
2939 }
2940
2941 if (client->conn_type != NC_CH_PERIOD) {
ravsz20789e12020-03-31 14:43:05 +02002942 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002943 /* UNLOCK */
2944 nc_server_ch_client_unlock(client);
2945 return -1;
2946 }
2947
2948 client->conn.period.period = period;
2949
2950 /* UNLOCK */
2951 nc_server_ch_client_unlock(client);
2952
2953 return 0;
2954}
2955
2956API int
2957nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002958{
2959 struct nc_ch_client *client;
2960
2961 if (!client_name) {
2962 ERRARG("client_name");
2963 return -1;
2964 }
2965
2966 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002967 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002968 if (!client) {
2969 return -1;
2970 }
2971
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002972 if (client->conn_type != NC_CH_PERIOD) {
ravsz20789e12020-03-31 14:43:05 +02002973 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002974 /* UNLOCK */
2975 nc_server_ch_client_unlock(client);
2976 return -1;
2977 }
2978
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002979 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002980
2981 /* UNLOCK */
2982 nc_server_ch_client_unlock(client);
2983
2984 return 0;
2985}
2986
2987API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002988nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002989{
2990 struct nc_ch_client *client;
2991
2992 if (!client_name) {
2993 ERRARG("client_name");
2994 return -1;
2995 }
2996
2997 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002998 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002999 if (!client) {
3000 return -1;
3001 }
3002
3003 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10003004 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003005 /* UNLOCK */
3006 nc_server_ch_client_unlock(client);
3007 return -1;
3008 }
3009
3010 client->conn.period.idle_timeout = idle_timeout;
3011
3012 /* UNLOCK */
3013 nc_server_ch_client_unlock(client);
3014
3015 return 0;
3016}
3017
3018API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003019nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
3020{
3021 struct nc_ch_client *client;
3022
3023 if (!client_name) {
3024 ERRARG("client_name");
3025 return -1;
3026 }
3027
3028 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003029 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003030 if (!client) {
3031 return -1;
3032 }
3033
3034 client->start_with = start_with;
3035
3036 /* UNLOCK */
3037 nc_server_ch_client_unlock(client);
3038
3039 return 0;
3040}
3041
3042API int
3043nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3044{
3045 struct nc_ch_client *client;
3046
3047 if (!client_name) {
3048 ERRARG("client_name");
3049 return -1;
3050 } else if (!max_attempts) {
3051 ERRARG("max_attempts");
3052 return -1;
3053 }
3054
3055 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003056 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003057 if (!client) {
3058 return -1;
3059 }
3060
3061 client->max_attempts = max_attempts;
3062
3063 /* UNLOCK */
3064 nc_server_ch_client_unlock(client);
3065
3066 return 0;
3067}
3068
3069/* client lock is expected to be held */
3070static NC_MSG_TYPE
Michal Vaskoadf30f02019-06-24 09:34:47 +02003071nc_connect_ch_endpt(struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01003072{
Michal Vasko71090fc2016-05-24 16:37:28 +02003073 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003074 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003075 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003076 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003077
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003078 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->ka, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01003079 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02003080 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003081 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00003082 /* no need to store the socket as pending any longer */
3083 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01003084
Michal Vasko131120a2018-05-29 15:44:02 +02003085 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003086 if (!(*session)) {
3087 ERRMEM;
3088 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003089 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02003090 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003091 }
3092 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01003093 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003094 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko66032bc2019-01-22 15:03:12 +01003095 (*session)->host = lydict_insert_zc(server_opts.ctx, ip_host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003096 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003097
Michal Vaskob05053d2016-01-22 16:12:06 +01003098 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003099#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003100 if (endpt->ti == NC_TI_LIBSSH) {
3101 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003102 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003103 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003104
Michal Vasko71090fc2016-05-24 16:37:28 +02003105 if (ret < 0) {
3106 msgtype = NC_MSG_ERROR;
3107 goto fail;
3108 } else if (!ret) {
3109 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003110 goto fail;
3111 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003112 } else
3113#endif
Radek Krejci53691be2016-02-22 13:58:37 +01003114#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003115 if (endpt->ti == NC_TI_OPENSSL) {
3116 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003117 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003118 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003119
Michal Vasko71090fc2016-05-24 16:37:28 +02003120 if (ret < 0) {
3121 msgtype = NC_MSG_ERROR;
3122 goto fail;
3123 } else if (!ret) {
3124 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003125 goto fail;
3126 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003127 } else
3128#endif
3129 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003130 ERRINT;
3131 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003132 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003133 goto fail;
3134 }
3135
3136 /* assign new SID atomically */
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02003137 (*session)->id = ATOMIC_INC(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003138
3139 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003140 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003141 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003142 goto fail;
3143 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003144
3145 nc_gettimespec_mono(&ts_cur);
3146 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3147 nc_gettimespec_real(&ts_cur);
3148 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003149 (*session)->status = NC_STATUS_RUNNING;
3150
Michal Vasko71090fc2016-05-24 16:37:28 +02003151 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003152
3153fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003154 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003155 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003156 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003157}
3158
Michal Vasko2e6defd2016-10-07 15:48:15 +02003159struct nc_ch_client_thread_arg {
3160 char *client_name;
3161 void (*session_clb)(const char *client_name, struct nc_session *new_session);
3162};
3163
3164static struct nc_ch_client *
3165nc_server_ch_client_with_endpt_lock(const char *name)
3166{
3167 struct nc_ch_client *client;
3168
3169 while (1) {
3170 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003171 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003172 if (!client) {
3173 return NULL;
3174 }
3175 if (client->ch_endpt_count) {
3176 return client;
3177 }
3178 /* no endpoints defined yet */
3179
3180 /* UNLOCK */
3181 nc_server_ch_client_unlock(client);
3182
3183 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3184 }
3185
3186 return NULL;
3187}
3188
3189static int
3190nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3191{
Michal Vasko3f05a092018-03-13 10:39:49 +01003192 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003193 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003194 struct timespec ts;
3195 struct nc_ch_client *client;
3196
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003197 session->flags |= NC_SESSION_CALLHOME;
3198
Michal Vasko2e6defd2016-10-07 15:48:15 +02003199 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003200 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003201
3202 /* give the session to the user */
3203 data->session_clb(data->client_name, session);
3204
3205 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003206 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003207 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003208
Michal Vaskoacf98472021-02-04 15:33:57 +01003209 r = pthread_cond_timedwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, &ts);
Michal Vasko3f05a092018-03-13 10:39:49 +01003210 if (!r) {
3211 /* we were woken up, something probably happened */
3212 if (session->status != NC_STATUS_RUNNING) {
3213 break;
3214 }
3215 } else if (r != ETIMEDOUT) {
3216 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3217 ret = -1;
3218 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003219 }
3220
Michal Vasko2e6defd2016-10-07 15:48:15 +02003221 /* check whether the client was not removed */
3222 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003223 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003224 if (!client) {
3225 /* client was removed, finish thread */
3226 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003227 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003228 ret = 1;
3229 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003230 }
3231
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003232 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003233 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003234 } else {
3235 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003236 }
3237
Michal Vasko9fb42272017-10-05 13:50:05 +02003238 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003239 if (!session->opts.server.ntf_status && idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003240 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3241 session->status = NC_STATUS_INVALID;
3242 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3243 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003244
3245 /* UNLOCK */
3246 nc_server_ch_client_unlock(client);
3247
3248 } while (session->status == NC_STATUS_RUNNING);
3249
Michal Vasko27377422018-03-15 08:59:35 +01003250 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003251 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01003252
Michal Vasko3f05a092018-03-13 10:39:49 +01003253 if (session->status == NC_STATUS_CLOSING) {
3254 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
3255 session->flags &= ~NC_SESSION_CALLHOME;
3256 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003257
Michal Vasko3f05a092018-03-13 10:39:49 +01003258 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003259}
3260
3261static void *
3262nc_ch_client_thread(void *arg)
3263{
3264 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3265 NC_MSG_TYPE msgtype;
3266 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003267 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003268 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003269 struct nc_ch_endpt *cur_endpt;
3270 struct nc_session *session;
3271 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003272 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003273 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003274
3275 /* LOCK */
3276 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3277 if (!client) {
3278 goto cleanup;
3279 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003280 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003281
3282 cur_endpt = &client->ch_endpts[0];
3283 cur_endpt_name = strdup(cur_endpt->name);
3284
Michal Vasko29af44b2016-10-13 10:59:55 +02003285 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003286 while (1) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02003287 msgtype = nc_connect_ch_endpt(cur_endpt, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003288
3289 if (msgtype == NC_MSG_HELLO) {
3290 /* UNLOCK */
3291 nc_server_ch_client_unlock(client);
3292
Michal Vasko29af44b2016-10-13 10:59:55 +02003293 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003294 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3295 goto cleanup;
3296 }
Michal Vasko4ec210b2020-04-16 09:32:01 +02003297 VRB("Call Home client \"%s\" session terminated, reconnecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003298
3299 /* LOCK */
3300 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3301 if (!client) {
3302 goto cleanup;
3303 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003304 if (client->id != client_id) {
3305 nc_server_ch_client_unlock(client);
3306 goto cleanup;
3307 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003308
3309 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003310 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003311 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003312 /* UNLOCK */
3313 nc_server_ch_client_unlock(client);
3314
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003315 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
3316 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3317 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003318
3319 /* LOCK */
3320 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3321 if (!client) {
3322 goto cleanup;
3323 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003324 if (client->id != client_id) {
3325 nc_server_ch_client_unlock(client);
3326 goto cleanup;
3327 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003328 }
3329
3330 /* set next endpoint to try */
3331 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003332 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003333 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003334 /* we keep the current one but due to unlock/lock we have to find it again */
3335 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3336 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3337 break;
3338 }
3339 }
3340 if (next_endpt_index >= client->ch_endpt_count) {
3341 /* endpoint was removed, start with the first one */
3342 next_endpt_index = 0;
3343 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003344 } else {
3345 /* just get a random index */
3346 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003347 }
3348
Michal Vasko2e6defd2016-10-07 15:48:15 +02003349 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003350 /* UNLOCK */
3351 nc_server_ch_client_unlock(client);
3352
Michal Vasko2e6defd2016-10-07 15:48:15 +02003353 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003354 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3355
Michal Vasko6bb116b2016-10-26 13:53:46 +02003356 /* LOCK */
3357 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3358 if (!client) {
3359 goto cleanup;
3360 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003361 if (client->id != client_id) {
3362 nc_server_ch_client_unlock(client);
3363 goto cleanup;
3364 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003365
Michal Vasko2e6defd2016-10-07 15:48:15 +02003366 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003367
3368 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003369 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3370 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003371 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003372 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003373 }
3374
Peter Feiged05f2252018-09-03 08:09:47 +00003375 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003376 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003377 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003378 cur_attempts = 0;
3379 } else if (cur_attempts == client->max_attempts) {
3380 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003381 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003382 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003383 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003384 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003385 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003386 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003387 }
3388
3389 cur_attempts = 0;
3390 } /* else we keep the current one */
3391 }
Peter Feiged05f2252018-09-03 08:09:47 +00003392
3393 cur_endpt = &client->ch_endpts[next_endpt_index];
3394 free(cur_endpt_name);
3395 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003396 }
3397
3398cleanup:
3399 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003400 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003401 free(data->client_name);
3402 free(data);
3403 return NULL;
3404}
3405
3406API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02003407nc_connect_ch_client_dispatch(const char *client_name, void (*session_clb)(const char *client_name,
3408 struct nc_session *new_session))
Michal Vasko3f05a092018-03-13 10:39:49 +01003409{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003410 int ret;
3411 pthread_t tid;
3412 struct nc_ch_client_thread_arg *arg;
3413
3414 if (!client_name) {
3415 ERRARG("client_name");
3416 return -1;
3417 } else if (!session_clb) {
3418 ERRARG("session_clb");
3419 return -1;
3420 }
3421
3422 arg = malloc(sizeof *arg);
3423 if (!arg) {
3424 ERRMEM;
3425 return -1;
3426 }
3427 arg->client_name = strdup(client_name);
3428 if (!arg->client_name) {
3429 ERRMEM;
3430 free(arg);
3431 return -1;
3432 }
3433 arg->session_clb = session_clb;
3434
3435 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3436 if (ret) {
3437 ERR("Creating a new thread failed (%s).", strerror(ret));
3438 free(arg->client_name);
3439 free(arg);
3440 return -1;
3441 }
3442 /* the thread now manages arg */
3443
3444 pthread_detach(tid);
3445
3446 return 0;
3447}
3448
Radek Krejci53691be2016-02-22 13:58:37 +01003449#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003450
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003451API time_t
3452nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003453{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003454 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003455 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003456 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003457 }
3458
Michal Vasko2e6defd2016-10-07 15:48:15 +02003459 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003460}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003461
3462API void
3463nc_session_set_notif_status(struct nc_session *session, int notif_status)
3464{
3465 if (!session || (session->side != NC_SERVER)) {
3466 ERRARG("session");
3467 return;
3468 }
3469
3470 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3471}
3472
3473API int
3474nc_session_get_notif_status(const struct nc_session *session)
3475{
3476 if (!session || (session->side != NC_SERVER)) {
3477 ERRARG("session");
3478 return 0;
3479 }
3480
3481 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003482}
Michal Vasko8f430592019-02-26 08:32:54 +01003483
3484API int
3485nc_session_is_callhome(const struct nc_session *session)
3486{
3487 if (!session || (session->side != NC_SERVER)) {
3488 ERRARG("session");
3489 return 0;
3490 }
3491
3492 if (session->flags & NC_SESSION_CALLHOME) {
3493 return 1;
3494 }
3495
3496 return 0;
3497}