blob: e4e987c41fd6f0f5a746e45423960a4e9e5caeaf [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 */
Olivier Matzac7fa2f2018-10-11 10:02:04 +020014#define _GNU_SOURCE /* signals, threads, SO_PEERCRED */
Michal Vasko086311b2016-01-08 09:53:11 +010015
16#include <stdint.h>
17#include <stdlib.h>
18#include <errno.h>
19#include <string.h>
20#include <poll.h>
21#include <sys/types.h>
22#include <sys/socket.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020023#include <sys/un.h>
24#include <netinet/in.h>
25#include <netinet/tcp.h>
Michal Vasko086311b2016-01-08 09:53:11 +010026#include <arpa/inet.h>
27#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010028#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010029#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010030#include <time.h>
Michal Vaskoade892d2017-02-22 13:40:35 +010031#include <signal.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020032#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010033
Michal Vasko1a38c862016-01-15 15:50:07 +010034#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010035#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020036#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010037
Michal Vaskob48aa812016-01-18 14:13:09 +010038struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010039#ifdef NC_ENABLED_SSH
40 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
41#endif
42 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020043 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
44 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010045};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010046
fanchanghu966f2de2016-07-21 02:28:57 -040047static nc_rpc_clb global_rpc_clb = NULL;
48
Michal Vasko3031aae2016-01-27 16:07:18 +010049struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010050nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010051{
52 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010053 struct nc_endpt *endpt = NULL;
54
Michal Vaskoddce1212019-05-24 09:58:49 +020055 if (!name) {
56 ERRARG("endpt_name");
57 return NULL;
58 }
59
Michal Vaskoade892d2017-02-22 13:40:35 +010060 /* WRITE LOCK */
61 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010062
63 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020064 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010065 endpt = &server_opts.endpts[i];
66 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010067 }
68 }
69
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010070 if (!endpt) {
71 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010072 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020073 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010074 return NULL;
75 }
76
Michal Vaskoe2713da2016-08-22 16:06:40 +020077 if (idx) {
78 *idx = i;
79 }
80
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010081 return endpt;
82}
83
Michal Vaskoadf30f02019-06-24 09:34:47 +020084struct nc_ch_endpt *
85nc_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 +010086{
Michal Vaskoadf30f02019-06-24 09:34:47 +020087 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020088 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020089 struct nc_ch_endpt *endpt = NULL;
90
91 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +020092
Michal Vaskoddce1212019-05-24 09:58:49 +020093 if (!name) {
94 ERRARG("client_name");
95 return NULL;
96 }
97
Michal Vasko2e6defd2016-10-07 15:48:15 +020098 /* READ LOCK */
99 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
100
101 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200102 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200103 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200104 if (!endpt_name && !ti) {
105 /* return only client */
106 break;
107 }
108 for (j = 0; j < client->ch_endpt_count; ++j) {
109 if (!strcmp(client->ch_endpts[j].name, endpt_name) && (!ti || (ti == client->ch_endpts[j].ti))) {
110 endpt = &client->ch_endpts[j];
111 break;
112 }
113 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200114 break;
115 }
116 }
117
118 if (!client) {
119 ERR("Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200120
Michal Vasko2e6defd2016-10-07 15:48:15 +0200121 /* READ UNLOCK */
122 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200123 } else if (endpt_name && ti && !endpt) {
124 ERR("Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
125
126 /* READ UNLOCK */
127 pthread_rwlock_unlock(&server_opts.ch_client_lock);
128 } else {
129 /* CH CLIENT LOCK */
130 pthread_mutex_lock(&client->lock);
131
132 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200133 }
134
Michal Vaskoadf30f02019-06-24 09:34:47 +0200135 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200136}
137
138void
139nc_server_ch_client_unlock(struct nc_ch_client *client)
140{
141 /* CH CLIENT UNLOCK */
142 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100143
144 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200145 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100146}
Michal Vasko086311b2016-01-08 09:53:11 +0100147
Michal Vasko1a38c862016-01-15 15:50:07 +0100148API void
149nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
150{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200151 if (!session) {
152 ERRARG("session");
153 return;
154 } else if (!reason) {
155 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100156 return;
157 }
158
Michal Vasko142cfea2017-08-07 10:12:11 +0200159 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
160 session->killed_by = 0;
161 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100162 session->term_reason = reason;
163}
164
Michal Vasko142cfea2017-08-07 10:12:11 +0200165API void
166nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
167{
168 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
169 ERRARG("session");
170 return;
171 } else if (!sid) {
172 ERRARG("sid");
173 return;
174 }
175
176 session->killed_by = sid;
177}
178
179API void
180nc_session_set_status(struct nc_session *session, NC_STATUS status)
181{
182 if (!session) {
183 ERRARG("session");
184 return;
185 } else if (!status) {
186 ERRARG("status");
187 return;
188 }
189
190 session->status = status;
191}
192
Michal Vasko086311b2016-01-08 09:53:11 +0100193int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200194nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100195{
Michal Vasko06c860d2018-07-09 16:08:52 +0200196 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100197 int is_ipv4, sock;
198 struct sockaddr_storage saddr;
199
200 struct sockaddr_in *saddr4;
201 struct sockaddr_in6 *saddr6;
202
203
204 if (!strchr(address, ':')) {
205 is_ipv4 = 1;
206 } else {
207 is_ipv4 = 0;
208 }
209
210 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
211 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100212 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100213 goto fail;
214 }
215
Michal Vaskobe52dc22018-10-17 09:28:17 +0200216 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200217 opt = 1;
218 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
219 ERR("Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
220 goto fail;
221 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100222 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
223 ERR("Could not set TCP_NODELAY socket option (%s).", strerror(errno));
224 goto fail;
225 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200226
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200227 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100228 goto fail;
229 }
230
231 bzero(&saddr, sizeof(struct sockaddr_storage));
232 if (is_ipv4) {
233 saddr4 = (struct sockaddr_in *)&saddr;
234
235 saddr4->sin_family = AF_INET;
236 saddr4->sin_port = htons(port);
237
238 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100239 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100240 goto fail;
241 }
242
243 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100244 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100245 goto fail;
246 }
247
248 } else {
249 saddr6 = (struct sockaddr_in6 *)&saddr;
250
251 saddr6->sin6_family = AF_INET6;
252 saddr6->sin6_port = htons(port);
253
254 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100255 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100256 goto fail;
257 }
258
259 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100260 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100261 goto fail;
262 }
263 }
264
Michal Vaskofb89d772016-01-08 12:25:35 +0100265 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100266 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100267 goto fail;
268 }
269
270 return sock;
271
272fail:
273 if (sock > -1) {
274 close(sock);
275 }
276
277 return -1;
278}
279
280int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200281nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
282{
283 struct sockaddr_un sun;
284 int sock = -1;
285
286 sock = socket(AF_UNIX, SOCK_STREAM, 0);
287 if (sock == -1) {
288 ERR("Failed to create socket (%s).", strerror(errno));
289 goto fail;
290 }
291
292 memset(&sun, 0, sizeof(sun));
293 sun.sun_family = AF_UNIX;
294 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
295
296 unlink(sun.sun_path);
297 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
298 ERR("Could not bind \"%s\" (%s).", address, strerror(errno));
299 goto fail;
300 }
301
302 if (opts->mode != (mode_t)-1) {
303 if (chmod(sun.sun_path, opts->mode) < 0) {
304 ERR("Failed to set unix socket permissions (%s).", strerror(errno));
305 goto fail;
306 }
307 }
308
309 if (opts->uid != (uid_t)-1 || opts->gid != (gid_t)-1) {
310 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
311 ERR("Failed to set unix socket uid/gid (%s).", strerror(errno));
312 goto fail;
313 }
314 }
315
316 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
317 ERR("Unable to start listening on \"%s\" (%s).", address, strerror(errno));
318 goto fail;
319 }
320
321 return sock;
322
323fail:
324 if (sock > -1) {
325 close(sock);
326 }
327
328 return -1;
329}
330
331int
Michal Vasko3031aae2016-01-27 16:07:18 +0100332nc_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 +0100333{
Michal Vaskof54cd352017-02-22 13:42:02 +0100334 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100335 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100336 struct pollfd *pfd;
337 struct sockaddr_storage saddr;
338 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100339 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100340
341 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100342 if (!pfd) {
343 ERRMEM;
344 return -1;
345 }
346
Michal Vaskoac2f6182017-01-30 14:32:03 +0100347 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200348 if (binds[i].sock < 0) {
349 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200350 continue;
351 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200352 if (binds[i].pollin) {
353 binds[i].pollin = 0;
354 /* leftover pollin */
355 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100356 break;
357 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100358 pfd[pfd_count].fd = binds[i].sock;
359 pfd[pfd_count].events = POLLIN;
360 pfd[pfd_count].revents = 0;
361
362 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100363 }
364
Michal Vasko0a3f3752016-10-13 14:58:38 +0200365 if (sock == -1) {
366 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100367 sigfillset(&sigmask);
368 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100369 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100370 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
371
Michal Vasko0a3f3752016-10-13 14:58:38 +0200372 if (!ret) {
373 /* we timeouted */
374 free(pfd);
375 return 0;
376 } else if (ret == -1) {
377 ERR("Poll failed (%s).", strerror(errno));
378 free(pfd);
379 return -1;
380 }
Michal Vasko086311b2016-01-08 09:53:11 +0100381
Michal Vaskoac2f6182017-01-30 14:32:03 +0100382 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
383 /* adjust i so that indices in binds and pfd always match */
384 while (binds[i].sock != pfd[j].fd) {
385 ++i;
386 }
387
388 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200389 --ret;
390
391 if (!ret) {
392 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100393 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200394 break;
395 } else {
396 /* just remember the event for next time */
397 binds[i].pollin = 1;
398 }
399 }
Michal Vasko086311b2016-01-08 09:53:11 +0100400 }
401 }
402 free(pfd);
403
404 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100405 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100406 return -1;
407 }
408
409 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100410 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100411 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100412 return -1;
413 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200414 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100415
Michal Vasko0190bc32016-03-02 15:47:49 +0100416 /* make the socket non-blocking */
417 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
418 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100419 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100420 return -1;
421 }
422
Michal Vasko3031aae2016-01-27 16:07:18 +0100423 if (idx) {
424 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100425 }
426
Michal Vasko086311b2016-01-08 09:53:11 +0100427 /* host was requested */
428 if (host) {
429 if (saddr.ss_family == AF_INET) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000430 *host = malloc(INET_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100431 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000432 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100433 ERR("inet_ntop failed (%s).", strerror(errno));
434 free(*host);
435 *host = NULL;
436 }
Michal Vasko086311b2016-01-08 09:53:11 +0100437
Michal Vasko4eb3c312016-03-01 14:09:37 +0100438 if (port) {
439 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
440 }
441 } else {
442 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100443 }
444 } else if (saddr.ss_family == AF_INET6) {
Jan Kundrát0f942e82018-02-14 14:52:00 +0100445 *host = malloc(INET6_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100446 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000447 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100448 ERR("inet_ntop failed (%s).", strerror(errno));
449 free(*host);
450 *host = NULL;
451 }
Michal Vasko086311b2016-01-08 09:53:11 +0100452
Michal Vasko4eb3c312016-03-01 14:09:37 +0100453 if (port) {
454 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
455 }
456 } else {
457 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100458 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200459 } else if (saddr.ss_family == AF_UNIX) {
460 *host = strdup(((struct sockaddr_un *)&saddr)->sun_path);
461 if (*host) {
462 if (port) {
463 *port = 0;
464 }
465 } else {
466 ERRMEM;
467 }
Michal Vasko086311b2016-01-08 09:53:11 +0100468 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100469 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100470 }
471 }
472
473 return ret;
474}
475
Michal Vasko05ba9df2016-01-13 14:40:27 +0100476static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100477nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100478{
479 const char *identifier = NULL, *version = NULL, *format = NULL;
480 char *model_data = NULL;
481 const struct lys_module *module;
482 struct nc_server_error *err;
483 struct lyd_node *child, *data = NULL;
Michal Vasko88639e92017-08-03 14:38:10 +0200484 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100485
486 LY_TREE_FOR(rpc->child, child) {
487 if (!strcmp(child->schema->name, "identifier")) {
488 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
489 } else if (!strcmp(child->schema->name, "version")) {
490 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500491 if (version && version[0] == '\0') {
492 version = NULL;
493 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100494 } else if (!strcmp(child->schema->name, "format")) {
495 format = ((struct lyd_node_leaf_list *)child)->value_str;
496 }
497 }
Michal Vaskoddce1212019-05-24 09:58:49 +0200498 VRB("Schema \"%s\" was requested.", identifier);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100499
500 /* check version */
501 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100502 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
503 nc_err_set_msg(err, "The requested version is not supported.", "en");
504 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100505 }
506
507 /* check and get module with the name identifier */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200508 module = ly_ctx_get_module(server_opts.ctx, identifier, version, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100509 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200510 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
511 }
512 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100513 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
514 nc_err_set_msg(err, "The requested schema was not found.", "en");
515 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100516 }
517
518 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100519 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100520 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL, 0, 0);
Radek Krejci90fba642016-12-07 15:59:45 +0100521 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100522 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100523 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100524 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
525 nc_err_set_msg(err, "The requested format is not supported.", "en");
526 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100527 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200528 if (!model_data) {
529 ERRINT;
530 return NULL;
531 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100532
Michal Vasko88639e92017-08-03 14:38:10 +0200533 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
534 if (!sdata) {
535 ERRINT;
536 free(model_data);
537 return NULL;
538 }
539
Radek Krejci539efb62016-08-24 15:05:16 +0200540 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
541 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100542 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100543 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200544 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100545 return NULL;
546 }
547
Radek Krejci36dfdb32016-09-01 16:56:35 +0200548 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100549}
550
551static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100552nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100553{
Michal Vasko428087d2016-01-14 16:04:28 +0100554 session->term_reason = NC_SESSION_TERM_CLOSED;
555 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100556}
557
Michal Vasko086311b2016-01-08 09:53:11 +0100558API int
559nc_server_init(struct ly_ctx *ctx)
560{
Michal Vasko88639e92017-08-03 14:38:10 +0200561 const struct lys_node *rpc;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000562 pthread_rwlockattr_t attr;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100563
Michal Vasko086311b2016-01-08 09:53:11 +0100564 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200565 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100566 return -1;
567 }
568
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100569 nc_init();
570
Michal Vasko05ba9df2016-01-13 14:40:27 +0100571 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200572 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
573 if (rpc && !rpc->priv) {
574 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100575 }
576
577 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200578 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
579 if (rpc && !rpc->priv) {
580 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100581 }
582
Michal Vasko086311b2016-01-08 09:53:11 +0100583 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100584
585 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500586 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100587
Frank Rimpler9f838b02018-07-25 06:44:03 +0000588 errno=0;
589
590 if (pthread_rwlockattr_init(&attr) == 0) {
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700591#if defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
Frank Rimpler9f838b02018-07-25 06:44:03 +0000592 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
593 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
594 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
595 }
596 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
597 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
598 }
599 } else {
600 ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
601 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700602#endif
Frank Rimpler9f838b02018-07-25 06:44:03 +0000603 pthread_rwlockattr_destroy(&attr);
604 } else {
605 ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
606 }
Michal Vasko086311b2016-01-08 09:53:11 +0100607 return 0;
608}
609
Michal Vaskob48aa812016-01-18 14:13:09 +0100610API void
611nc_server_destroy(void)
612{
Radek Krejci658782b2016-12-04 22:04:55 +0100613 unsigned int i;
614
615 for (i = 0; i < server_opts.capabilities_count; i++) {
616 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
617 }
618 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200619 server_opts.capabilities = NULL;
620 server_opts.capabilities_count = 0;
621
Radek Krejci53691be2016-02-22 13:58:37 +0100622#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100623 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200624 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100625#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100626#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100627 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
628 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
629 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200630 server_opts.passwd_auth_data = NULL;
631 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100632
Michal Vasko17dfda92016-12-01 14:06:16 +0100633 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100634
635 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
636 server_opts.hostkey_data_free(server_opts.hostkey_data);
637 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200638 server_opts.hostkey_data = NULL;
639 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100640#endif
641#ifdef NC_ENABLED_TLS
642 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
643 server_opts.server_cert_data_free(server_opts.server_cert_data);
644 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200645 server_opts.server_cert_data = NULL;
646 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100647 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
648 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
649 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200650 server_opts.trusted_cert_list_data = NULL;
651 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100652#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100653 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100654}
655
Michal Vasko086311b2016-01-08 09:53:11 +0100656API int
657nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
658{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200659 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
660 ERRARG("basic_mode");
661 return -1;
662 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
663 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100664 return -1;
665 }
666
667 server_opts.wd_basic_mode = basic_mode;
668 server_opts.wd_also_supported = also_supported;
669 return 0;
670}
671
Michal Vasko1a38c862016-01-15 15:50:07 +0100672API void
Michal Vasko55f03972016-04-13 08:56:01 +0200673nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
674{
675 if (!basic_mode && !also_supported) {
676 ERRARG("basic_mode and also_supported");
677 return;
678 }
679
680 if (basic_mode) {
681 *basic_mode = server_opts.wd_basic_mode;
682 }
683 if (also_supported) {
684 *also_supported = server_opts.wd_also_supported;
685 }
686}
687
Michal Vasko55f03972016-04-13 08:56:01 +0200688API int
Radek Krejci658782b2016-12-04 22:04:55 +0100689nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200690{
Radek Krejci658782b2016-12-04 22:04:55 +0100691 const char **new;
692
693 if (!value || !value[0]) {
694 ERRARG("value must not be empty");
695 return EXIT_FAILURE;
696 }
697
698 server_opts.capabilities_count++;
699 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
700 if (!new) {
701 ERRMEM;
702 return EXIT_FAILURE;
703 }
704 server_opts.capabilities = new;
705 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
706
707 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200708}
709
Michal Vasko1a38c862016-01-15 15:50:07 +0100710API void
Michal Vasko086311b2016-01-08 09:53:11 +0100711nc_server_set_hello_timeout(uint16_t hello_timeout)
712{
Michal Vasko086311b2016-01-08 09:53:11 +0100713 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100714}
715
Michal Vasko55f03972016-04-13 08:56:01 +0200716API uint16_t
717nc_server_get_hello_timeout(void)
718{
719 return server_opts.hello_timeout;
720}
721
Michal Vasko1a38c862016-01-15 15:50:07 +0100722API void
Michal Vasko086311b2016-01-08 09:53:11 +0100723nc_server_set_idle_timeout(uint16_t idle_timeout)
724{
Michal Vasko086311b2016-01-08 09:53:11 +0100725 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100726}
727
Michal Vasko55f03972016-04-13 08:56:01 +0200728API uint16_t
729nc_server_get_idle_timeout(void)
730{
731 return server_opts.idle_timeout;
732}
733
Michal Vasko71090fc2016-05-24 16:37:28 +0200734API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100735nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100736{
Michal Vasko71090fc2016-05-24 16:37:28 +0200737 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200738 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200739
Michal Vasko45e53ae2016-04-07 11:46:03 +0200740 if (!server_opts.ctx) {
741 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200742 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200743 } else if (fdin < 0) {
744 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200745 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200746 } else if (fdout < 0) {
747 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200748 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200749 } else if (!username) {
750 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200751 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200752 } else if (!session) {
753 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200754 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100755 }
756
757 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200758 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100759 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100760 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200761 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100762 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100763 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100764
Michal Vasko086311b2016-01-08 09:53:11 +0100765 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100766 (*session)->ti_type = NC_TI_FD;
767 (*session)->ti.fd.in = fdin;
768 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100769
770 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100771 (*session)->flags = NC_SESSION_SHAREDCTX;
772 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100773
Michal Vaskob48aa812016-01-18 14:13:09 +0100774 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +0100775 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100776
Michal Vasko086311b2016-01-08 09:53:11 +0100777 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200778 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200779 if (msgtype != NC_MSG_HELLO) {
780 nc_session_free(*session, NULL);
781 *session = NULL;
782 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100783 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200784
785 nc_gettimespec_mono(&ts_cur);
786 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
787 nc_gettimespec_real(&ts_cur);
788 (*session)->opts.server.session_start = ts_cur.tv_sec;
789
Michal Vasko1a38c862016-01-15 15:50:07 +0100790 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100791
Michal Vasko71090fc2016-05-24 16:37:28 +0200792 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100793}
Michal Vasko9e036d52016-01-08 10:49:26 +0100794
Michal Vaskob30b99c2016-07-26 11:35:43 +0200795static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100796nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
797{
798 uint8_t q_last;
799
800 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
801 ERRINT;
802 return;
803 }
804
805 /* get a unique queue value (by adding 1 to the last added value, if any) */
806 if (ps->queue_len) {
807 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
808 *id = ps->queue[q_last] + 1;
809 } else {
810 *id = 0;
811 }
812
813 /* add the id into the queue */
814 ++ps->queue_len;
815 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
816 ps->queue[q_last] = *id;
817}
818
819static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200820nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
821{
Michal Vasko74c345f2018-02-07 10:37:11 +0100822 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200823
824 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100825 /* get the actual queue idx */
826 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200827
828 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100829 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200830 /* another equal value, simply cannot be */
831 ERRINT;
832 }
Michal Vaskod8340032018-02-12 14:41:00 +0100833 if (found == 2) {
834 /* move the following values */
835 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
836 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100837 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200838 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100839 if (i == 0) {
840 found = 1;
841 } else {
842 /* this is not okay, our id is in the middle of the queue */
843 found = 2;
844 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200845 }
846 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200847 if (!found) {
848 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100849 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200850 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100851
Michal Vasko103fe632018-02-12 16:37:45 +0100852 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100853 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100854 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100855 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
856 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200857}
858
Michal Vaskof04a52a2016-04-07 10:52:10 +0200859int
Michal Vasko26043172016-07-26 14:08:59 +0200860nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200861{
862 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200863 struct timespec ts;
864
Michal Vasko77a6abe2017-10-05 10:02:20 +0200865 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100866 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200867
868 /* LOCK */
869 ret = pthread_mutex_timedlock(&ps->lock, &ts);
870 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200871 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200872 return -1;
873 }
874
Michal Vasko74c345f2018-02-07 10:37:11 +0100875 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100876 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100877 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100878 pthread_mutex_unlock(&ps->lock);
879 return -1;
880 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100881
882 /* add ourselves into the queue */
883 nc_ps_queue_add_id(ps, id);
Michal Vasko91290952019-09-27 11:30:55 +0200884 DBG("PS 0x%p TID %lu queue: added %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), *id,
885 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200886
887 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200888 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200889 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100890 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200891
892 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
893 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +0530894 /**
895 * This may happen when another thread releases the lock and broadcasts the condition
896 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
897 * but when actually this thread was ready for condition.
898 */
preetbhansali629dfc42018-12-17 16:04:40 +0530899 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +0530900 break;
901 }
Michal Vasko66032bc2019-01-22 15:03:12 +0100902
Michal Vasko26043172016-07-26 14:08:59 +0200903 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200904 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200905 nc_ps_queue_remove_id(ps, *id);
906 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200907 return -1;
908 }
909 }
910
Michal Vaskobe86fe32016-04-07 10:43:03 +0200911 /* UNLOCK */
912 pthread_mutex_unlock(&ps->lock);
913
914 return 0;
915}
916
Michal Vaskof04a52a2016-04-07 10:52:10 +0200917int
Michal Vasko26043172016-07-26 14:08:59 +0200918nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200919{
920 int ret;
921 struct timespec ts;
922
Michal Vasko77a6abe2017-10-05 10:02:20 +0200923 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100924 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200925
926 /* LOCK */
927 ret = pthread_mutex_timedlock(&ps->lock, &ts);
928 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200929 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200930 ret = -1;
931 }
932
Michal Vaskob30b99c2016-07-26 11:35:43 +0200933 /* we must be the first, it was our turn after all, right? */
934 if (ps->queue[ps->queue_begin] != id) {
935 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200936 /* UNLOCK */
937 if (!ret) {
938 pthread_mutex_unlock(&ps->lock);
939 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200940 return -1;
941 }
942
Michal Vaskobe86fe32016-04-07 10:43:03 +0200943 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200944 nc_ps_queue_remove_id(ps, id);
Michal Vasko91290952019-09-27 11:30:55 +0200945 DBG("PS 0x%p TID %lu queue: removed %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), id,
946 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200947
948 /* broadcast to all other threads that the queue moved */
949 pthread_cond_broadcast(&ps->cond);
950
Michal Vaskobe86fe32016-04-07 10:43:03 +0200951 /* UNLOCK */
952 if (!ret) {
953 pthread_mutex_unlock(&ps->lock);
954 }
955
956 return ret;
957}
958
Michal Vasko428087d2016-01-14 16:04:28 +0100959API struct nc_pollsession *
960nc_ps_new(void)
961{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100962 struct nc_pollsession *ps;
963
964 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100965 if (!ps) {
966 ERRMEM;
967 return NULL;
968 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200969 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100970 pthread_mutex_init(&ps->lock, NULL);
971
972 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100973}
974
975API void
976nc_ps_free(struct nc_pollsession *ps)
977{
fanchanghu3d4e7212017-08-09 09:42:30 +0800978 uint16_t i;
979
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100980 if (!ps) {
981 return;
982 }
983
Michal Vaskobe86fe32016-04-07 10:43:03 +0200984 if (ps->queue_len) {
985 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
986 }
987
fanchanghu3d4e7212017-08-09 09:42:30 +0800988 for (i = 0; i < ps->session_count; i++) {
989 free(ps->sessions[i]);
990 }
991
Michal Vasko428087d2016-01-14 16:04:28 +0100992 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100993 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200994 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100995
Michal Vasko428087d2016-01-14 16:04:28 +0100996 free(ps);
997}
998
999API int
1000nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1001{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001002 uint8_t q_id;
1003
Michal Vasko45e53ae2016-04-07 11:46:03 +02001004 if (!ps) {
1005 ERRARG("ps");
1006 return -1;
1007 } else if (!session) {
1008 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001009 return -1;
1010 }
1011
Michal Vasko48a63ed2016-03-01 09:48:21 +01001012 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001013 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001014 return -1;
1015 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001016
Michal Vasko428087d2016-01-14 16:04:28 +01001017 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001018 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001019 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001020 ERRMEM;
1021 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001022 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001023 return -1;
1024 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001025 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1026 if (!ps->sessions[ps->session_count - 1]) {
1027 ERRMEM;
1028 --ps->session_count;
1029 /* UNLOCK */
1030 nc_ps_unlock(ps, q_id, __func__);
1031 return -1;
1032 }
1033 ps->sessions[ps->session_count - 1]->session = session;
1034 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001035
Michal Vasko48a63ed2016-03-01 09:48:21 +01001036 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001037 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001038}
1039
Michal Vasko48a63ed2016-03-01 09:48:21 +01001040static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001041_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001042{
1043 uint16_t i;
1044
Radek Krejcid5f978f2016-03-03 13:14:45 +01001045 if (index >= 0) {
1046 i = (uint16_t)index;
1047 goto remove;
1048 }
Michal Vasko428087d2016-01-14 16:04:28 +01001049 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001050 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001051remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001052 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001053 if (i <= ps->session_count) {
1054 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001055 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001056 }
1057 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001058 free(ps->sessions);
1059 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001060 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001061 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001062 return 0;
1063 }
1064 }
1065
Michal Vaskof0537d82016-01-29 14:42:38 +01001066 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001067}
1068
Michal Vasko48a63ed2016-03-01 09:48:21 +01001069API int
1070nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1071{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001072 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001073 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001074
Michal Vasko45e53ae2016-04-07 11:46:03 +02001075 if (!ps) {
1076 ERRARG("ps");
1077 return -1;
1078 } else if (!session) {
1079 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001080 return -1;
1081 }
1082
1083 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001084 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001085 return -1;
1086 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001087
Radek Krejcid5f978f2016-03-03 13:14:45 +01001088 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001089
1090 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001091 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001092
Michal Vaskobe86fe32016-04-07 10:43:03 +02001093 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001094}
1095
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001096API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001097nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001098{
1099 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001100 struct nc_session *ret = NULL;
1101
1102 if (!ps) {
1103 ERRARG("ps");
1104 return NULL;
1105 }
1106
1107 /* LOCK */
1108 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1109 return NULL;
1110 }
1111
Michal Vasko4871c9d2017-10-09 14:48:39 +02001112 if (idx < ps->session_count) {
1113 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001114 }
1115
1116 /* UNLOCK */
1117 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1118
1119 return ret;
1120}
1121
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001122API uint16_t
1123nc_ps_session_count(struct nc_pollsession *ps)
1124{
Michal Vasko47003942019-03-14 12:25:23 +01001125 uint8_t q_id;
1126 uint16_t session_count;
1127
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001128 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001129 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001130 return 0;
1131 }
1132
Michal Vasko47003942019-03-14 12:25:23 +01001133 /* LOCK (just for memory barrier so that we read the current value) */
1134 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1135 return 0;
1136 }
1137
1138 session_count = ps->session_count;
1139
1140 /* UNLOCK */
1141 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1142
1143 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001144}
1145
Michal Vasko131120a2018-05-29 15:44:02 +02001146/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001147 * returns: NC_PSPOLL_ERROR,
1148 * NC_PSPOLL_BAD_RPC,
1149 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1150 * NC_PSPOLL_RPC
1151 */
1152static int
Michal Vasko131120a2018-05-29 15:44:02 +02001153nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001154{
1155 struct lyxml_elem *xml = NULL;
1156 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001157 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001158 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001159
Michal Vasko45e53ae2016-04-07 11:46:03 +02001160 if (!session) {
1161 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001162 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001163 } else if (!rpc) {
1164 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001165 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001166 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001167 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001168 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001169 }
1170
Michal Vasko131120a2018-05-29 15:44:02 +02001171 msgtype = nc_read_msg_io(session, io_timeout, &xml, 0);
Michal Vasko428087d2016-01-14 16:04:28 +01001172
1173 switch (msgtype) {
1174 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001175 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001176 if (!*rpc) {
1177 ERRMEM;
1178 goto error;
1179 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001180
Radek Krejcif93c7d42016-04-06 13:41:15 +02001181 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001182 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1183 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001184 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001185 /* parsing RPC failed */
Michal Vaskoc9970242018-02-14 16:03:35 +01001186 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
Michal Vasko131120a2018-05-29 15:44:02 +02001187 ret = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001188 nc_server_reply_free(reply);
Michal Vaskod057b272020-02-07 10:49:05 +01001189 if (ret != NC_MSG_REPLY) {
Michal Vasko8fe604c2020-02-10 15:25:04 +01001190 ERR("Session %u: failed to write reply (%s).", session->id, nc_msgtype2str[ret]);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001191 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001192 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1193 } else {
1194 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001195 }
Michal Vasko428087d2016-01-14 16:04:28 +01001196 (*rpc)->root = xml;
1197 break;
1198 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001199 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001200 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001201 goto error;
1202 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001203 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001204 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001205 goto error;
1206 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001207 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001208 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001209 goto error;
1210 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001211 /* NC_MSG_ERROR,
Michal Vasko131120a2018-05-29 15:44:02 +02001212 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg_io()
Michal Vasko428087d2016-01-14 16:04:28 +01001213 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001214 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001215 break;
1216 }
1217
Michal Vasko71090fc2016-05-24 16:37:28 +02001218 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001219
1220error:
1221 /* cleanup */
1222 lyxml_free(server_opts.ctx, xml);
1223
Michal Vasko71090fc2016-05-24 16:37:28 +02001224 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001225}
1226
fanchanghu966f2de2016-07-21 02:28:57 -04001227API void
1228nc_set_global_rpc_clb(nc_rpc_clb clb)
1229{
1230 global_rpc_clb = clb;
1231}
1232
Radek Krejci93e80222016-10-03 13:34:25 +02001233API NC_MSG_TYPE
1234nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1235{
Michal Vasko131120a2018-05-29 15:44:02 +02001236 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001237
1238 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001239 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001240 ERRARG("session");
1241 return NC_MSG_ERROR;
1242 } else if (!notif || !notif->tree || !notif->eventtime) {
1243 ERRARG("notif");
1244 return NC_MSG_ERROR;
1245 }
1246
Michal Vasko131120a2018-05-29 15:44:02 +02001247 /* we do not need RPC lock for this, IO lock will be acquired properly */
1248 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001249 if (ret != NC_MSG_NOTIF) {
1250 ERR("Session %u: failed to write notification (%s).", session->id, nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001251 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001252
Michal Vasko131120a2018-05-29 15:44:02 +02001253 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001254}
1255
Michal Vasko131120a2018-05-29 15:44:02 +02001256/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001257 * returns: NC_PSPOLL_ERROR,
1258 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1259 * NC_PSPOLL_REPLY_ERROR,
1260 * 0
1261 */
1262static int
Michal Vasko131120a2018-05-29 15:44:02 +02001263nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001264{
1265 nc_rpc_clb clb;
1266 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001267 struct lys_node *rpc_act = NULL;
1268 struct lyd_node *next, *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001269 int ret = 0;
1270 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001271
Michal Vasko4a827e52016-03-03 10:59:00 +01001272 if (!rpc) {
1273 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001274 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001275 }
1276
Michal Vasko90e8e692016-07-13 12:27:57 +02001277 if (rpc->tree->schema->nodetype == LYS_RPC) {
1278 /* RPC */
1279 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001280 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001281 /* action */
1282 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1283 if (elem->schema->nodetype == LYS_ACTION) {
1284 rpc_act = elem->schema;
1285 break;
1286 }
1287 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001288 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001289 if (!rpc_act) {
1290 ERRINT;
1291 return NC_PSPOLL_ERROR;
1292 }
1293 }
1294
1295 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001296 if (!global_rpc_clb) {
1297 /* no callback, reply with a not-implemented error */
1298 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
1299 } else {
1300 reply = global_rpc_clb(rpc->tree, session);
1301 }
Michal Vasko428087d2016-01-14 16:04:28 +01001302 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001303 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001304 reply = clb(rpc->tree, session);
1305 }
1306
1307 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001308 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001309 }
Michal Vasko131120a2018-05-29 15:44:02 +02001310 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->root, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001311 if (reply->type == NC_RPL_ERROR) {
1312 ret |= NC_PSPOLL_REPLY_ERROR;
1313 }
1314 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001315
Michal Vasko131120a2018-05-29 15:44:02 +02001316 if (r != NC_MSG_REPLY) {
Michal Vasko8fe604c2020-02-10 15:25:04 +01001317 ERR("Session %u: failed to write reply (%s).", session->id, nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001318 ret |= NC_PSPOLL_ERROR;
1319 }
Michal Vasko428087d2016-01-14 16:04:28 +01001320
1321 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1322 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1323 session->status = NC_STATUS_INVALID;
1324 }
1325
Michal Vasko71090fc2016-05-24 16:37:28 +02001326 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001327}
1328
Michal Vasko131120a2018-05-29 15:44:02 +02001329/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001330 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1331 * NC_PSPOLL_ERROR, (msg filled)
1332 * NC_PSPOLL_TIMEOUT,
1333 * NC_PSPOLL_RPC (some application data available),
1334 * NC_PSPOLL_SSH_CHANNEL,
1335 * NC_PSPOLL_SSH_MSG
1336 */
1337static int
Michal Vasko131120a2018-05-29 15:44:02 +02001338nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001339{
Michal Vasko9a327362017-01-11 11:31:46 +01001340 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001341 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001342#ifdef NC_ENABLED_SSH
1343 struct nc_session *new;
1344#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001345
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001346 /* check timeout first */
1347 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001348 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001349 sprintf(msg, "session idle timeout elapsed");
1350 session->status = NC_STATUS_INVALID;
1351 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1352 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1353 }
1354
Michal Vasko131120a2018-05-29 15:44:02 +02001355 r = nc_session_io_lock(session, io_timeout, __func__);
1356 if (r < 0) {
1357 sprintf(msg, "session IO lock failed to be acquired");
1358 return NC_PSPOLL_ERROR;
1359 } else if (!r) {
1360 return NC_PSPOLL_TIMEOUT;
1361 }
1362
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001363 switch (session->ti_type) {
1364#ifdef NC_ENABLED_SSH
1365 case NC_TI_LIBSSH:
1366 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001367 if (r == SSH_EOF) {
1368 sprintf(msg, "SSH channel unexpected EOF");
1369 session->status = NC_STATUS_INVALID;
1370 session->term_reason = NC_SESSION_TERM_DROPPED;
1371 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1372 } else if (r == SSH_ERROR) {
1373 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001374 session->status = NC_STATUS_INVALID;
1375 session->term_reason = NC_SESSION_TERM_OTHER;
1376 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001377 } else if (!r) {
1378 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1379 /* new SSH message */
1380 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1381 if (session->ti.libssh.next) {
1382 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1383 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1384 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1385 /* new NETCONF SSH channel */
1386 ret = NC_PSPOLL_SSH_CHANNEL;
1387 break;
1388 }
1389 }
1390 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001391 break;
1392 }
1393 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001394
Michal Vasko8dcaa882017-10-19 14:28:42 +02001395 /* just some SSH message */
1396 ret = NC_PSPOLL_SSH_MSG;
1397 } else {
1398 ret = NC_PSPOLL_TIMEOUT;
1399 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001400 } else {
1401 /* we have some application data */
1402 ret = NC_PSPOLL_RPC;
1403 }
1404 break;
1405#endif
1406#ifdef NC_ENABLED_TLS
1407 case NC_TI_OPENSSL:
1408 r = SSL_pending(session->ti.tls);
1409 if (!r) {
1410 /* no data pending in the SSL buffer, poll fd */
1411 pfd.fd = SSL_get_rfd(session->ti.tls);
1412 if (pfd.fd < 0) {
1413 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1414 ret = NC_PSPOLL_ERROR;
1415 break;
1416 }
1417 pfd.events = POLLIN;
1418 pfd.revents = 0;
1419 r = poll(&pfd, 1, 0);
1420
1421 if ((r < 0) && (errno != EINTR)) {
1422 sprintf(msg, "poll failed (%s)", strerror(errno));
1423 session->status = NC_STATUS_INVALID;
1424 ret = NC_PSPOLL_ERROR;
1425 } else if (r > 0) {
1426 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1427 sprintf(msg, "communication socket unexpectedly closed");
1428 session->status = NC_STATUS_INVALID;
1429 session->term_reason = NC_SESSION_TERM_DROPPED;
1430 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1431 } else if (pfd.revents & POLLERR) {
1432 sprintf(msg, "communication socket error");
1433 session->status = NC_STATUS_INVALID;
1434 session->term_reason = NC_SESSION_TERM_OTHER;
1435 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1436 } else {
1437 ret = NC_PSPOLL_RPC;
1438 }
1439 } else {
1440 ret = NC_PSPOLL_TIMEOUT;
1441 }
1442 } else {
1443 ret = NC_PSPOLL_RPC;
1444 }
1445 break;
1446#endif
1447 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001448 case NC_TI_UNIX:
1449 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001450 pfd.events = POLLIN;
1451 pfd.revents = 0;
1452 r = poll(&pfd, 1, 0);
1453
1454 if ((r < 0) && (errno != EINTR)) {
1455 sprintf(msg, "poll failed (%s)", strerror(errno));
1456 session->status = NC_STATUS_INVALID;
1457 ret = NC_PSPOLL_ERROR;
1458 } else if (r > 0) {
1459 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1460 sprintf(msg, "communication socket unexpectedly closed");
1461 session->status = NC_STATUS_INVALID;
1462 session->term_reason = NC_SESSION_TERM_DROPPED;
1463 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1464 } else if (pfd.revents & POLLERR) {
1465 sprintf(msg, "communication socket error");
1466 session->status = NC_STATUS_INVALID;
1467 session->term_reason = NC_SESSION_TERM_OTHER;
1468 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1469 } else {
1470 ret = NC_PSPOLL_RPC;
1471 }
1472 } else {
1473 ret = NC_PSPOLL_TIMEOUT;
1474 }
1475 break;
1476 case NC_TI_NONE:
1477 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1478 ret = NC_PSPOLL_ERROR;
1479 break;
1480 }
1481
Michal Vasko131120a2018-05-29 15:44:02 +02001482 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001483 return ret;
1484}
1485
1486API int
1487nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1488{
1489 int ret, r;
1490 uint8_t q_id;
1491 uint16_t i, j;
1492 char msg[256];
1493 struct timespec ts_timeout, ts_cur;
1494 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001495 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001496 struct nc_server_rpc *rpc = NULL;
1497
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001498 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001499 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001500 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001501 }
1502
Michal Vaskoade892d2017-02-22 13:40:35 +01001503 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001504 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001505 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001506 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001507
Michal Vaskoade892d2017-02-22 13:40:35 +01001508 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001509 nc_ps_unlock(ps, q_id, __func__);
1510 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001511 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001512
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001513 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001514 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001515 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001516 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001517 nc_addtimespec(&ts_timeout, timeout);
1518 }
1519
1520 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001521 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001522 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001523 if (ps->last_event_session == ps->session_count - 1) {
1524 i = j = 0;
1525 } else {
1526 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001527 }
Michal Vasko9a327362017-01-11 11:31:46 +01001528 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001529 cur_ps_session = ps->sessions[i];
1530 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001531
Michal Vasko131120a2018-05-29 15:44:02 +02001532 /* SESSION RPC LOCK */
1533 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001534 if (r == -1) {
1535 ret = NC_PSPOLL_ERROR;
1536 } else if (r == 1) {
1537 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001538 switch (cur_ps_session->state) {
1539 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001540 if (cur_session->status == NC_STATUS_RUNNING) {
1541 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001542 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001543
Michal Vasko131120a2018-05-29 15:44:02 +02001544 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001545 switch (ret) {
1546 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1547 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001548 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001549 break;
1550 case NC_PSPOLL_ERROR:
1551 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001552 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001553 break;
1554 case NC_PSPOLL_TIMEOUT:
1555#ifdef NC_ENABLED_SSH
1556 case NC_PSPOLL_SSH_CHANNEL:
1557 case NC_PSPOLL_SSH_MSG:
1558#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001559 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001560 break;
1561 case NC_PSPOLL_RPC:
1562 /* let's keep the state busy, we are not done with this session */
1563 break;
1564 }
1565 } else {
1566 /* session is not fine, let the caller know */
1567 ret = NC_PSPOLL_SESSION_TERM;
1568 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1569 ret |= NC_PSPOLL_SESSION_ERROR;
1570 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001571 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001572 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001573 break;
1574 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001575 /* it definitely should not be busy because we have the lock */
1576 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001577 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001578 break;
1579 case NC_PS_STATE_INVALID:
1580 /* we got it locked, but it will be freed, let it be */
1581 ret = NC_PSPOLL_TIMEOUT;
1582 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001583 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001584
Michal Vasko131120a2018-05-29 15:44:02 +02001585 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001586 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001587 /* SESSION RPC UNLOCK */
1588 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001589 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001590 } else {
1591 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001592 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001593 }
Michal Vasko428087d2016-01-14 16:04:28 +01001594
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001595 /* something happened */
1596 if (ret != NC_PSPOLL_TIMEOUT) {
1597 break;
1598 }
1599
Michal Vasko9a327362017-01-11 11:31:46 +01001600 if (i == ps->session_count - 1) {
1601 i = 0;
1602 } else {
1603 ++i;
1604 }
1605 } while (i != j);
1606
Michal Vaskoade892d2017-02-22 13:40:35 +01001607 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001608 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001609 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001610 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001611 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001612
1613 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1614 /* final timeout */
1615 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001616 }
Michal Vasko428087d2016-01-14 16:04:28 +01001617 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001618 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001619
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001620 /* do we want to return the session? */
1621 switch (ret) {
1622 case NC_PSPOLL_RPC:
1623 case NC_PSPOLL_SESSION_TERM:
1624 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1625#ifdef NC_ENABLED_SSH
1626 case NC_PSPOLL_SSH_CHANNEL:
1627 case NC_PSPOLL_SSH_MSG:
1628#endif
1629 if (session) {
1630 *session = cur_session;
1631 }
1632 ps->last_event_session = i;
1633 break;
1634 default:
1635 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001636 }
Michal Vasko428087d2016-01-14 16:04:28 +01001637
Michal Vaskoade892d2017-02-22 13:40:35 +01001638 /* PS UNLOCK */
1639 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001640
Michal Vasko131120a2018-05-29 15:44:02 +02001641 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001642 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001643 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001644 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1645 if (cur_session->status != NC_STATUS_RUNNING) {
1646 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001647 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001648 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001649 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001650 }
1651 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001652 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001653
Michal Vasko7f1ee932018-10-11 09:41:42 +02001654 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001655 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001656 if (cur_session->status != NC_STATUS_RUNNING) {
1657 ret |= NC_PSPOLL_SESSION_TERM;
1658 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1659 ret |= NC_PSPOLL_SESSION_ERROR;
1660 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001661 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001662 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001663 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001664 }
Michal Vasko428087d2016-01-14 16:04:28 +01001665 }
Michal Vasko7f1ee932018-10-11 09:41:42 +02001666 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001667
Michal Vasko131120a2018-05-29 15:44:02 +02001668 /* SESSION RPC UNLOCK */
1669 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001670 }
1671
Michal Vasko48a63ed2016-03-01 09:48:21 +01001672 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001673}
1674
Michal Vaskod09eae62016-02-01 10:32:52 +01001675API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001676nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001677{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001678 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001679 uint16_t i;
1680 struct nc_session *session;
1681
Michal Vasko9a25e932016-02-01 10:36:42 +01001682 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001683 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001684 return;
1685 }
1686
Michal Vasko48a63ed2016-03-01 09:48:21 +01001687 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001688 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001689 return;
1690 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001691
Michal Vasko48a63ed2016-03-01 09:48:21 +01001692 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001693 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001694 nc_session_free(ps->sessions[i]->session, data_free);
1695 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001696 }
1697 free(ps->sessions);
1698 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001699 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001700 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001701 } else {
1702 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001703 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1704 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001705 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001706 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001707 continue;
1708 }
1709
1710 ++i;
1711 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001712 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001713
1714 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001715 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001716}
1717
Radek Krejci53691be2016-02-22 13:58:37 +01001718#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001719
Michal Vasko5f352c52019-07-10 16:12:06 +02001720static int
1721nc_accept_unix(struct nc_session *session, int sock)
1722{
Claus Klein22091912020-01-20 13:45:47 +01001723#ifdef SO_PEERCRED
Michal Vasko5f352c52019-07-10 16:12:06 +02001724 const struct passwd *pw;
1725 struct ucred ucred;
1726 char *username;
1727 socklen_t len;
Michal Vasko5f352c52019-07-10 16:12:06 +02001728 session->ti_type = NC_TI_UNIX;
1729
1730 len = sizeof(ucred);
1731 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
1732 ERR("Failed to get credentials from unix socket (%s).",
1733 strerror(errno));
1734 close(sock);
1735 return -1;
1736 }
1737
1738 pw = getpwuid(ucred.uid);
1739 if (pw == NULL) {
1740 ERR("Failed to find username for uid=%u (%s).\n", ucred.uid,
1741 strerror(errno));
1742 close(sock);
1743 return -1;
1744 }
1745
1746 username = strdup(pw->pw_name);
1747 if (username == NULL) {
1748 ERRMEM;
1749 close(sock);
1750 return -1;
1751 }
1752 session->username = lydict_insert_zc(server_opts.ctx, username);
1753
1754 session->ti.unixsock.sock = sock;
1755
1756 return 1;
Claus Klein22091912020-01-20 13:45:47 +01001757#else
1758 return -1;
1759#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02001760}
1761
Michal Vaskoe2713da2016-08-22 16:06:40 +02001762API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001763nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001764{
Michal Vasko3031aae2016-01-27 16:07:18 +01001765 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001766 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001767
Michal Vasko45e53ae2016-04-07 11:46:03 +02001768 if (!name) {
1769 ERRARG("name");
1770 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001771 }
1772
Michal Vaskoade892d2017-02-22 13:40:35 +01001773 /* BIND LOCK */
1774 pthread_mutex_lock(&server_opts.bind_lock);
1775
1776 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001777 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001778
1779 /* check name uniqueness */
1780 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001781 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001782 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001783 ret = -1;
1784 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001785 }
1786 }
1787
Michal Vasko3031aae2016-01-27 16:07:18 +01001788 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001789 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001790 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001791 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001792 ret = -1;
1793 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001794 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001795 memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001796 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001797 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001798 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1799 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1800 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001801
Michal Vaskoe2713da2016-08-22 16:06:40 +02001802 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001803 if (!server_opts.binds) {
1804 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001805 ret = -1;
1806 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001807 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001808
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001809 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001810 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1811
1812 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001813#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001814 case NC_TI_LIBSSH:
1815 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1816 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1817 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001818 ret = -1;
1819 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001820 }
1821 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1822 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1823 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02001824 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001825 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001826#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001827#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001828 case NC_TI_OPENSSL:
1829 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1830 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1831 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001832 ret = -1;
1833 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001834 }
1835 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001836#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001837 case NC_TI_UNIX:
1838 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1839 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1840 ERRMEM;
1841 ret = -1;
1842 goto cleanup;
1843 }
1844 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1845 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1846 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1847 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001848 default:
1849 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001850 ret = -1;
1851 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001852 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001853
Michal Vaskoade892d2017-02-22 13:40:35 +01001854cleanup:
1855 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001856 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001857
Michal Vaskoade892d2017-02-22 13:40:35 +01001858 /* BIND UNLOCK */
1859 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001860
Michal Vaskoade892d2017-02-22 13:40:35 +01001861 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001862}
1863
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001864API int
Michal Vasko59050372016-11-22 14:33:55 +01001865nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001866{
1867 uint32_t i;
1868 int ret = -1;
1869
Michal Vaskoade892d2017-02-22 13:40:35 +01001870 /* BIND LOCK */
1871 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001872
Michal Vaskoade892d2017-02-22 13:40:35 +01001873 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001874 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001875
Michal Vasko59050372016-11-22 14:33:55 +01001876 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001877 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001878 for (i = 0; i < server_opts.endpt_count; ++i) {
1879 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001880 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001881#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001882 case NC_TI_LIBSSH:
1883 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1884 free(server_opts.endpts[i].opts.ssh);
1885 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001886#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001887#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001888 case NC_TI_OPENSSL:
1889 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1890 free(server_opts.endpts[i].opts.tls);
1891 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001892#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001893 case NC_TI_UNIX:
1894 free(server_opts.endpts[i].opts.unixsock);
1895 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001896 default:
1897 ERRINT;
1898 /* won't get here ...*/
1899 break;
1900 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001901 ret = 0;
1902 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001903 free(server_opts.endpts);
1904 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001905
1906 /* remove all binds */
1907 for (i = 0; i < server_opts.endpt_count; ++i) {
1908 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1909 if (server_opts.binds[i].sock > -1) {
1910 close(server_opts.binds[i].sock);
1911 }
1912 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001913 free(server_opts.binds);
1914 server_opts.binds = NULL;
1915
Michal Vasko3031aae2016-01-27 16:07:18 +01001916 server_opts.endpt_count = 0;
1917
Michal Vasko1a38c862016-01-15 15:50:07 +01001918 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001919 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001920 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001921 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001922 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001923 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001924 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001925#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001926 case NC_TI_LIBSSH:
1927 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1928 free(server_opts.endpts[i].opts.ssh);
1929 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001930#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001931#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001932 case NC_TI_OPENSSL:
1933 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1934 free(server_opts.endpts[i].opts.tls);
1935 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001936#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001937 case NC_TI_UNIX:
1938 free(server_opts.endpts[i].opts.unixsock);
1939 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001940 default:
1941 ERRINT;
1942 break;
1943 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001944
Michal Vaskoe2713da2016-08-22 16:06:40 +02001945 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001946 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1947 if (server_opts.binds[i].sock > -1) {
1948 close(server_opts.binds[i].sock);
1949 }
1950
1951 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001952 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001953 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001954 free(server_opts.binds);
1955 server_opts.binds = NULL;
1956 free(server_opts.endpts);
1957 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001958 } else if (i < server_opts.endpt_count) {
1959 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1960 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001961 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001962
1963 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001964 if (name) {
1965 break;
1966 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001967 }
1968 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001969 }
1970
Michal Vaskoade892d2017-02-22 13:40:35 +01001971 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001972 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001973
Michal Vaskoade892d2017-02-22 13:40:35 +01001974 /* BIND UNLOCK */
1975 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001976
1977 return ret;
1978}
1979
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001980API int
1981nc_server_endpt_count(void)
1982{
1983 return server_opts.endpt_count;
1984}
1985
Michal Vasko1b5973e2020-01-30 16:05:46 +01001986API int
1987nc_server_is_endpt(const char *name)
1988{
1989 uint16_t i;
1990 int found = 0;
1991
Michal Vaskofb1724b2020-01-31 11:02:00 +01001992 if (!name) {
1993 return found;
1994 }
1995
Michal Vasko1b5973e2020-01-30 16:05:46 +01001996 /* ENDPT READ LOCK */
1997 pthread_rwlock_rdlock(&server_opts.endpt_lock);
1998
1999 /* check name uniqueness */
2000 for (i = 0; i < server_opts.endpt_count; ++i) {
2001 if (!strcmp(server_opts.endpts[i].name, name)) {
2002 found = 1;
2003 break;
2004 }
2005 }
2006
2007 /* ENDPT UNLOCK */
2008 pthread_rwlock_unlock(&server_opts.endpt_lock);
2009
2010 return found;
2011}
2012
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002013int
2014nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2015{
2016 struct nc_endpt *endpt;
2017 struct nc_bind *bind = NULL;
2018 uint16_t i;
2019 int sock = -1, set_addr, ret = 0;
2020
2021 if (!endpt_name) {
2022 ERRARG("endpt_name");
2023 return -1;
2024 } else if ((!address && !port) || (address && port)) {
2025 ERRARG("address and port");
2026 return -1;
2027 }
2028
2029 if (address) {
2030 set_addr = 1;
2031 } else {
2032 set_addr = 0;
2033 }
2034
2035 /* BIND LOCK */
2036 pthread_mutex_lock(&server_opts.bind_lock);
2037
2038 /* ENDPT LOCK */
2039 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2040 if (!endpt) {
2041 /* BIND UNLOCK */
2042 pthread_mutex_unlock(&server_opts.bind_lock);
2043 return -1;
2044 }
2045
2046 bind = &server_opts.binds[i];
2047
2048 if (set_addr) {
2049 port = bind->port;
2050 } else {
2051 address = bind->address;
2052 }
2053
2054 if (!set_addr && endpt->ti == NC_TI_UNIX) {
2055 ret = -1;
2056 goto cleanup;
2057 }
2058
2059 /* we have all the information we need to create a listening socket */
2060 if (address && (port || endpt->ti == NC_TI_UNIX)) {
2061 /* create new socket, close the old one */
2062 if (endpt->ti == NC_TI_UNIX)
2063 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
2064 else
2065 sock = nc_sock_listen_inet(address, port, &endpt->ka);
2066 if (sock == -1) {
2067 ret = -1;
2068 goto cleanup;
2069 }
2070
2071 if (bind->sock > -1) {
2072 close(bind->sock);
2073 }
2074 bind->sock = sock;
2075 } /* else we are just setting address or port */
2076
2077 if (set_addr) {
2078 lydict_remove(server_opts.ctx, bind->address);
2079 bind->address = lydict_insert(server_opts.ctx, address, 0);
2080 } else {
2081 bind->port = port;
2082 }
2083
2084 if (sock > -1) {
2085#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
2086 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
2087#elif defined(NC_ENABLED_SSH)
2088 VRB("Listening on %s:%u for SSH connections.", address, port);
2089#else
2090 VRB("Listening on %s:%u for TLS connections.", address, port);
2091#endif
2092 }
2093
2094cleanup:
2095 /* ENDPT UNLOCK */
2096 pthread_rwlock_unlock(&server_opts.endpt_lock);
2097
2098 /* BIND UNLOCK */
2099 pthread_mutex_unlock(&server_opts.bind_lock);
2100
2101 return ret;
2102}
2103
2104API int
2105nc_server_endpt_set_address(const char *endpt_name, const char *address)
2106{
2107 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2108}
2109
2110API int
2111nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2112{
2113 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2114}
2115
2116API int
2117nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2118{
2119 struct nc_endpt *endpt;
2120 uint16_t i;
2121 int ret = 0;
2122
2123 if (!endpt_name) {
2124 ERRARG("endpt_name");
2125 return -1;
2126 } else if (mode == 0) {
2127 ERRARG("mode");
2128 return -1;
2129 }
2130
2131 /* ENDPT LOCK */
2132 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2133 if (!endpt)
2134 return -1;
2135
2136 if (endpt->ti != NC_TI_UNIX) {
2137 ret = -1;
2138 goto cleanup;
2139 }
2140
2141 endpt->opts.unixsock->mode = mode;
2142 endpt->opts.unixsock->uid = uid;
2143 endpt->opts.unixsock->gid = gid;
2144
2145cleanup:
2146 /* ENDPT UNLOCK */
2147 pthread_rwlock_unlock(&server_opts.endpt_lock);
2148
2149 return ret;
2150}
2151
2152API int
2153nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2154{
2155 struct nc_endpt *endpt;
2156 int ret = 0;
2157
2158 if (!endpt_name) {
2159 ERRARG("endpt_name");
2160 return -1;
2161 }
2162
2163 /* ENDPT LOCK */
2164 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2165 if (!endpt) {
2166 return -1;
2167 }
2168
2169 endpt->ka.enabled = (enable ? 1 : 0);
2170
2171 /* ENDPT UNLOCK */
2172 pthread_rwlock_unlock(&server_opts.endpt_lock);
2173
2174 return ret;
2175}
2176
2177API int
2178nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2179{
2180 struct nc_endpt *endpt;
2181 int ret = 0;
2182
2183 if (!endpt_name) {
2184 ERRARG("endpt_name");
2185 return -1;
2186 }
2187
2188 /* ENDPT LOCK */
2189 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2190 if (!endpt) {
2191 return -1;
2192 }
2193
2194 if (idle_time > -1) {
2195 endpt->ka.idle_time = idle_time;
2196 }
2197 if (max_probes > -1) {
2198 endpt->ka.max_probes = max_probes;
2199 }
2200 if (probe_interval > -1) {
2201 endpt->ka.probe_interval = probe_interval;
2202 }
2203
2204 /* ENDPT UNLOCK */
2205 pthread_rwlock_unlock(&server_opts.endpt_lock);
2206
2207 return ret;
2208}
2209
Michal Vasko71090fc2016-05-24 16:37:28 +02002210API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002211nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002212{
Michal Vasko71090fc2016-05-24 16:37:28 +02002213 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002214 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002215 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002216 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002217 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002218
Michal Vasko45e53ae2016-04-07 11:46:03 +02002219 if (!server_opts.ctx) {
2220 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002221 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002222 } else if (!session) {
2223 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002224 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002225 }
2226
Michal Vaskoade892d2017-02-22 13:40:35 +01002227 /* BIND LOCK */
2228 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002229
2230 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002231 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002232 /* BIND UNLOCK */
2233 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002234 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002235 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002236
Michal Vaskoe2713da2016-08-22 16:06:40 +02002237 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002238 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002239 /* BIND UNLOCK */
2240 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002241 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002242 if (!ret) {
2243 return NC_MSG_WOULDBLOCK;
2244 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002245 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002246 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002247
2248 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2249 /* ENDPT READ LOCK */
2250 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2251
2252 /* BIND UNLOCK */
2253 pthread_mutex_unlock(&server_opts.bind_lock);
2254
Michal Vaskob48aa812016-01-18 14:13:09 +01002255 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002256
Michal Vasko131120a2018-05-29 15:44:02 +02002257 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002258 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002259 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002260 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002261 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002262 msgtype = NC_MSG_ERROR;
2263 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002264 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002265 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002266 (*session)->ctx = server_opts.ctx;
2267 (*session)->flags = NC_SESSION_SHAREDCTX;
2268 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
2269 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002270
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002271 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002272#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002273 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2274 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002275 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002276 if (ret < 0) {
2277 msgtype = NC_MSG_ERROR;
2278 goto cleanup;
2279 } else if (!ret) {
2280 msgtype = NC_MSG_WOULDBLOCK;
2281 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002282 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002283 } else
2284#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002285#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002286 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2287 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002288 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002289 if (ret < 0) {
2290 msgtype = NC_MSG_ERROR;
2291 goto cleanup;
2292 } else if (!ret) {
2293 msgtype = NC_MSG_WOULDBLOCK;
2294 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002295 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002296 } else
2297#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002298 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2299 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2300 ret = nc_accept_unix(*session, sock);
2301 if (ret < 0) {
2302 msgtype = NC_MSG_ERROR;
2303 goto cleanup;
2304 }
2305 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002306 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002307 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002308 msgtype = NC_MSG_ERROR;
2309 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002310 }
2311
Michal Vasko2cc4c682016-03-01 09:16:48 +01002312 (*session)->data = NULL;
2313
Michal Vaskoade892d2017-02-22 13:40:35 +01002314 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002315 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002316
Michal Vaskob48aa812016-01-18 14:13:09 +01002317 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002318 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002319
Michal Vasko9e036d52016-01-08 10:49:26 +01002320 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002321 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002322 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002323 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002324 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002325 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002326 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002327
2328 nc_gettimespec_mono(&ts_cur);
2329 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2330 nc_gettimespec_real(&ts_cur);
2331 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002332 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002333
Michal Vasko71090fc2016-05-24 16:37:28 +02002334 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002335
Michal Vasko71090fc2016-05-24 16:37:28 +02002336cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002337 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002338 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002339
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002340 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002341 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002342 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002343}
2344
Michal Vaskoadf30f02019-06-24 09:34:47 +02002345/* client is expected to be locked */
2346static int
2347_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 +02002348{
2349 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002350 int ret = -1;
2351
2352 if (!endpt_name) {
2353 /* remove all endpoints */
2354 for (i = 0; i < client->ch_endpt_count; ++i) {
2355 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2356 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2357 if (client->ch_endpts[i].sock_pending != -1) {
2358 close(client->ch_endpts[i].sock_pending);
2359 }
2360 switch (client->ch_endpts[i].ti) {
2361#ifdef NC_ENABLED_SSH
2362 case NC_TI_LIBSSH:
2363 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2364 free(client->ch_endpts[i].opts.ssh);
2365 break;
2366#endif
2367#ifdef NC_ENABLED_TLS
2368 case NC_TI_OPENSSL:
2369 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2370 free(client->ch_endpts[i].opts.tls);
2371 break;
2372#endif
2373 default:
2374 ERRINT;
2375 /* won't get here ...*/
2376 break;
2377 }
2378 }
2379 free(client->ch_endpts);
2380 client->ch_endpts = NULL;
2381 client->ch_endpt_count = 0;
2382
2383 ret = 0;
2384 } else {
2385 for (i = 0; i < client->ch_endpt_count; ++i) {
2386 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
2387 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2388 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2389 if (client->ch_endpts[i].sock_pending != -1) {
2390 close(client->ch_endpts[i].sock_pending);
2391 }
2392 switch (client->ch_endpts[i].ti) {
2393#ifdef NC_ENABLED_SSH
2394 case NC_TI_LIBSSH:
2395 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2396 free(client->ch_endpts[i].opts.ssh);
2397 break;
2398#endif
2399#ifdef NC_ENABLED_TLS
2400 case NC_TI_OPENSSL:
2401 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2402 free(client->ch_endpts[i].opts.tls);
2403 break;
2404#endif
2405 default:
2406 ERRINT;
2407 /* won't get here ...*/
2408 break;
2409 }
2410
2411 /* move last endpoint to the empty space */
2412 --client->ch_endpt_count;
2413 if (i < client->ch_endpt_count) {
2414 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2415 } else if (!server_opts.ch_client_count) {
2416 free(server_opts.ch_clients);
2417 server_opts.ch_clients = NULL;
2418 }
2419
2420 ret = 0;
2421 break;
2422 }
2423 }
2424 }
2425
2426 return ret;
2427}
2428
2429API int
2430nc_server_ch_add_client(const char *name)
2431{
2432 uint16_t i;
2433 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002434
2435 if (!name) {
2436 ERRARG("name");
2437 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002438 }
2439
2440 /* WRITE LOCK */
2441 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2442
2443 /* check name uniqueness */
2444 for (i = 0; i < server_opts.ch_client_count; ++i) {
2445 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2446 ERR("Call Home client \"%s\" already exists.", name);
2447 /* WRITE UNLOCK */
2448 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2449 return -1;
2450 }
2451 }
2452
2453 ++server_opts.ch_client_count;
2454 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2455 if (!server_opts.ch_clients) {
2456 ERRMEM;
2457 /* WRITE UNLOCK */
2458 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2459 return -1;
2460 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002461 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002462
Michal Vaskoadf30f02019-06-24 09:34:47 +02002463 client->name = lydict_insert(server_opts.ctx, name, 0);
2464 client->id = ATOMIC_INC(&server_opts.new_client_id);
2465 client->ch_endpts = NULL;
2466 client->ch_endpt_count = 0;
2467 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002468
Michal Vasko2e6defd2016-10-07 15:48:15 +02002469 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002470 client->start_with = NC_CH_FIRST_LISTED;
2471 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002472
Michal Vaskoadf30f02019-06-24 09:34:47 +02002473 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002474
2475 /* WRITE UNLOCK */
2476 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2477
2478 return 0;
2479}
2480
2481API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002482nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002483{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002484 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002485 int ret = -1;
2486
2487 /* WRITE LOCK */
2488 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2489
Michal Vaskoadf30f02019-06-24 09:34:47 +02002490 if (!name) {
2491 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002492 for (i = 0; i < server_opts.ch_client_count; ++i) {
2493 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2494
2495 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002496 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002497
2498 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002499 ret = 0;
2500 }
2501 free(server_opts.ch_clients);
2502 server_opts.ch_clients = NULL;
2503
2504 server_opts.ch_client_count = 0;
2505
2506 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002507 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002508 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002509 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002510 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2511
Michal Vasko2e6defd2016-10-07 15:48:15 +02002512 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002513 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002514
2515 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2516
2517 /* move last client and endpoint(s) to the empty space */
2518 --server_opts.ch_client_count;
2519 if (i < server_opts.ch_client_count) {
2520 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002521 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002522 } else if (!server_opts.ch_client_count) {
2523 free(server_opts.ch_clients);
2524 server_opts.ch_clients = NULL;
2525 }
2526
2527 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002528 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002529 }
2530 }
2531 }
2532
2533 /* WRITE UNLOCK */
2534 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2535
2536 return ret;
2537}
2538
2539API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002540nc_server_ch_is_client(const char *name)
2541{
2542 uint16_t i;
2543 int found = 0;
2544
2545 if (!name) {
2546 return found;
2547 }
2548
2549 /* READ LOCK */
2550 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2551
2552 /* check name uniqueness */
2553 for (i = 0; i < server_opts.ch_client_count; ++i) {
2554 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2555 found = 1;
2556 break;
2557 }
2558 }
2559
2560 /* UNLOCK */
2561 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2562
2563 return found;
2564}
2565
2566API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002567nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002568{
2569 uint16_t i;
2570 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002571 struct nc_ch_endpt *endpt;
2572 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002573
2574 if (!client_name) {
2575 ERRARG("client_name");
2576 return -1;
2577 } else if (!endpt_name) {
2578 ERRARG("endpt_name");
2579 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002580 } else if (!ti) {
2581 ERRARG("ti");
2582 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002583 }
2584
2585 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002586 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002587 if (!client) {
2588 return -1;
2589 }
2590
2591 for (i = 0; i < client->ch_endpt_count; ++i) {
2592 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2593 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002594 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002595 }
2596 }
2597
2598 ++client->ch_endpt_count;
2599 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2600 if (!client->ch_endpts) {
2601 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002602 goto cleanup;
2603 }
2604 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2605
2606 memset(endpt, 0, sizeof *client->ch_endpts);
2607 endpt->name = lydict_insert(server_opts.ctx, endpt_name, 0);
2608 endpt->ti = ti;
2609 endpt->sock_pending = -1;
2610 endpt->ka.idle_time = 1;
2611 endpt->ka.max_probes = 10;
2612 endpt->ka.probe_interval = 5;
2613
2614 switch (ti) {
2615#ifdef NC_ENABLED_SSH
2616 case NC_TI_LIBSSH:
2617 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2618 if (!endpt->opts.ssh) {
2619 ERRMEM;
2620 goto cleanup;
2621 }
2622 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2623 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002624 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002625 break;
2626#endif
2627#ifdef NC_ENABLED_TLS
2628 case NC_TI_OPENSSL:
2629 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2630 if (!endpt->opts.tls) {
2631 ERRMEM;
2632 goto cleanup;
2633 }
2634 break;
2635#endif
2636 default:
2637 ERRINT;
2638 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002639 }
2640
Michal Vaskoadf30f02019-06-24 09:34:47 +02002641 /* success */
2642 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002643
Michal Vaskoadf30f02019-06-24 09:34:47 +02002644cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002645 /* UNLOCK */
2646 nc_server_ch_client_unlock(client);
2647
Michal Vaskoadf30f02019-06-24 09:34:47 +02002648 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002649}
2650
2651API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002652nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002653{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002654 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002655 struct nc_ch_client *client;
2656
2657 if (!client_name) {
2658 ERRARG("client_name");
2659 return -1;
2660 }
2661
2662 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002663 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002664 if (!client) {
2665 return -1;
2666 }
2667
Michal Vaskoadf30f02019-06-24 09:34:47 +02002668 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002669
2670 /* UNLOCK */
2671 nc_server_ch_client_unlock(client);
2672
2673 return ret;
2674}
2675
2676API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002677nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2678{
2679 uint16_t i;
2680 struct nc_ch_client *client = NULL;
2681 int found = 0;
2682
2683 if (!client_name || !endpt_name) {
2684 return found;
2685 }
2686
2687 /* READ LOCK */
2688 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2689
2690 for (i = 0; i < server_opts.ch_client_count; ++i) {
2691 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2692 client = &server_opts.ch_clients[i];
2693 break;
2694 }
2695 }
2696
2697 if (!client) {
2698 goto cleanup;
2699 }
2700
2701 for (i = 0; i < client->ch_endpt_count; ++i) {
2702 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2703 found = 1;
2704 break;
2705 }
2706 }
2707
2708cleanup:
2709 /* UNLOCK */
2710 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2711 return found;
2712}
2713
2714API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002715nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2716{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002717 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002718 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002719
2720 if (!client_name) {
2721 ERRARG("client_name");
2722 return -1;
2723 } else if (!endpt_name) {
2724 ERRARG("endpt_name");
2725 return -1;
2726 } else if (!address) {
2727 ERRARG("address");
2728 return -1;
2729 }
2730
2731 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002732 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2733 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002734 return -1;
2735 }
2736
Michal Vaskoadf30f02019-06-24 09:34:47 +02002737 lydict_remove(server_opts.ctx, endpt->address);
2738 endpt->address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002739
2740 /* UNLOCK */
2741 nc_server_ch_client_unlock(client);
2742
Michal Vaskoadf30f02019-06-24 09:34:47 +02002743 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002744}
2745
2746API int
2747nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2748{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002749 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002750 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002751
2752 if (!client_name) {
2753 ERRARG("client_name");
2754 return -1;
2755 } else if (!endpt_name) {
2756 ERRARG("endpt_name");
2757 return -1;
2758 } else if (!port) {
2759 ERRARG("port");
2760 return -1;
2761 }
2762
2763 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002764 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2765 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002766 return -1;
2767 }
2768
Michal Vaskoadf30f02019-06-24 09:34:47 +02002769 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002770
2771 /* UNLOCK */
2772 nc_server_ch_client_unlock(client);
2773
Michal Vaskoadf30f02019-06-24 09:34:47 +02002774 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002775}
2776
2777API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002778nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2779{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002780 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002781 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002782
2783 if (!client_name) {
2784 ERRARG("client_name");
2785 return -1;
2786 } else if (!endpt_name) {
2787 ERRARG("endpt_name");
2788 return -1;
2789 }
2790
2791 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002792 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2793 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002794 return -1;
2795 }
2796
Michal Vaskoadf30f02019-06-24 09:34:47 +02002797 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002798
2799 /* UNLOCK */
2800 nc_server_ch_client_unlock(client);
2801
Michal Vasko9af829a2019-09-12 13:50:00 +02002802 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002803}
2804
2805API int
2806nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
2807 int probe_interval)
2808{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002809 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002810 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002811
2812 if (!client_name) {
2813 ERRARG("client_name");
2814 return -1;
2815 } else if (!endpt_name) {
2816 ERRARG("endpt_name");
2817 return -1;
2818 }
2819
2820 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002821 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2822 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002823 return -1;
2824 }
2825
Michal Vaskoadf30f02019-06-24 09:34:47 +02002826 if (idle_time > -1) {
2827 endpt->ka.idle_time = idle_time;
2828 }
2829 if (max_probes > -1) {
2830 endpt->ka.max_probes = max_probes;
2831 }
2832 if (probe_interval > -1) {
2833 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002834 }
2835
2836 /* UNLOCK */
2837 nc_server_ch_client_unlock(client);
2838
Michal Vasko9af829a2019-09-12 13:50:00 +02002839 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002840}
2841
2842API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002843nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2844{
2845 struct nc_ch_client *client;
2846
2847 if (!client_name) {
2848 ERRARG("client_name");
2849 return -1;
2850 } else if (!conn_type) {
2851 ERRARG("conn_type");
2852 return -1;
2853 }
2854
2855 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002856 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002857 if (!client) {
2858 return -1;
2859 }
2860
2861 if (client->conn_type != conn_type) {
2862 client->conn_type = conn_type;
2863
2864 /* set default options */
2865 switch (conn_type) {
2866 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002867 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002868 break;
2869 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002870 client->conn.period.period = 60;
2871 client->conn.period.anchor_time = 0;
2872 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002873 break;
2874 default:
2875 ERRINT;
2876 break;
2877 }
2878 }
2879
2880 /* UNLOCK */
2881 nc_server_ch_client_unlock(client);
2882
2883 return 0;
2884}
2885
2886API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002887nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
2888{
2889 struct nc_ch_client *client;
2890
2891 if (!client_name) {
2892 ERRARG("client_name");
2893 return -1;
2894 } else if (!period) {
2895 ERRARG("period");
2896 return -1;
2897 }
2898
2899 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002900 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002901 if (!client) {
2902 return -1;
2903 }
2904
2905 if (client->conn_type != NC_CH_PERIOD) {
ravsz20789e12020-03-31 14:43:05 +02002906 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002907 /* UNLOCK */
2908 nc_server_ch_client_unlock(client);
2909 return -1;
2910 }
2911
2912 client->conn.period.period = period;
2913
2914 /* UNLOCK */
2915 nc_server_ch_client_unlock(client);
2916
2917 return 0;
2918}
2919
2920API int
2921nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002922{
2923 struct nc_ch_client *client;
2924
2925 if (!client_name) {
2926 ERRARG("client_name");
2927 return -1;
2928 }
2929
2930 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002931 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002932 if (!client) {
2933 return -1;
2934 }
2935
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002936 if (client->conn_type != NC_CH_PERIOD) {
ravsz20789e12020-03-31 14:43:05 +02002937 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002938 /* UNLOCK */
2939 nc_server_ch_client_unlock(client);
2940 return -1;
2941 }
2942
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002943 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002944
2945 /* UNLOCK */
2946 nc_server_ch_client_unlock(client);
2947
2948 return 0;
2949}
2950
2951API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002952nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002953{
2954 struct nc_ch_client *client;
2955
2956 if (!client_name) {
2957 ERRARG("client_name");
2958 return -1;
2959 }
2960
2961 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002962 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002963 if (!client) {
2964 return -1;
2965 }
2966
2967 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002968 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002969 /* UNLOCK */
2970 nc_server_ch_client_unlock(client);
2971 return -1;
2972 }
2973
2974 client->conn.period.idle_timeout = idle_timeout;
2975
2976 /* UNLOCK */
2977 nc_server_ch_client_unlock(client);
2978
2979 return 0;
2980}
2981
2982API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002983nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2984{
2985 struct nc_ch_client *client;
2986
2987 if (!client_name) {
2988 ERRARG("client_name");
2989 return -1;
2990 }
2991
2992 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002993 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002994 if (!client) {
2995 return -1;
2996 }
2997
2998 client->start_with = start_with;
2999
3000 /* UNLOCK */
3001 nc_server_ch_client_unlock(client);
3002
3003 return 0;
3004}
3005
3006API int
3007nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3008{
3009 struct nc_ch_client *client;
3010
3011 if (!client_name) {
3012 ERRARG("client_name");
3013 return -1;
3014 } else if (!max_attempts) {
3015 ERRARG("max_attempts");
3016 return -1;
3017 }
3018
3019 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003020 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003021 if (!client) {
3022 return -1;
3023 }
3024
3025 client->max_attempts = max_attempts;
3026
3027 /* UNLOCK */
3028 nc_server_ch_client_unlock(client);
3029
3030 return 0;
3031}
3032
3033/* client lock is expected to be held */
3034static NC_MSG_TYPE
Michal Vaskoadf30f02019-06-24 09:34:47 +02003035nc_connect_ch_endpt(struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01003036{
Michal Vasko71090fc2016-05-24 16:37:28 +02003037 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003038 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003039 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003040 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003041
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003042 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->ka, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01003043 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02003044 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003045 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00003046 /* no need to store the socket as pending any longer */
3047 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01003048
Michal Vasko131120a2018-05-29 15:44:02 +02003049 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003050 if (!(*session)) {
3051 ERRMEM;
3052 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003053 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02003054 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003055 }
3056 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01003057 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003058 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko66032bc2019-01-22 15:03:12 +01003059 (*session)->host = lydict_insert_zc(server_opts.ctx, ip_host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003060 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003061
Michal Vaskob05053d2016-01-22 16:12:06 +01003062 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003063#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003064 if (endpt->ti == NC_TI_LIBSSH) {
3065 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003066 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003067 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003068
Michal Vasko71090fc2016-05-24 16:37:28 +02003069 if (ret < 0) {
3070 msgtype = NC_MSG_ERROR;
3071 goto fail;
3072 } else if (!ret) {
3073 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003074 goto fail;
3075 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003076 } else
3077#endif
Radek Krejci53691be2016-02-22 13:58:37 +01003078#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003079 if (endpt->ti == NC_TI_OPENSSL) {
3080 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003081 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003082 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003083
Michal Vasko71090fc2016-05-24 16:37:28 +02003084 if (ret < 0) {
3085 msgtype = NC_MSG_ERROR;
3086 goto fail;
3087 } else if (!ret) {
3088 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003089 goto fail;
3090 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003091 } else
3092#endif
3093 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003094 ERRINT;
3095 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003096 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003097 goto fail;
3098 }
3099
3100 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01003101 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003102
3103 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003104 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003105 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003106 goto fail;
3107 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003108
3109 nc_gettimespec_mono(&ts_cur);
3110 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3111 nc_gettimespec_real(&ts_cur);
3112 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003113 (*session)->status = NC_STATUS_RUNNING;
3114
Michal Vasko71090fc2016-05-24 16:37:28 +02003115 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003116
3117fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003118 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003119 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003120 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003121}
3122
Michal Vasko2e6defd2016-10-07 15:48:15 +02003123struct nc_ch_client_thread_arg {
3124 char *client_name;
3125 void (*session_clb)(const char *client_name, struct nc_session *new_session);
3126};
3127
3128static struct nc_ch_client *
3129nc_server_ch_client_with_endpt_lock(const char *name)
3130{
3131 struct nc_ch_client *client;
3132
3133 while (1) {
3134 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003135 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003136 if (!client) {
3137 return NULL;
3138 }
3139 if (client->ch_endpt_count) {
3140 return client;
3141 }
3142 /* no endpoints defined yet */
3143
3144 /* UNLOCK */
3145 nc_server_ch_client_unlock(client);
3146
3147 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3148 }
3149
3150 return NULL;
3151}
3152
3153static int
3154nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3155{
Michal Vasko3f05a092018-03-13 10:39:49 +01003156 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003157 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003158 struct timespec ts;
3159 struct nc_ch_client *client;
3160
3161 /* session created, initialize condition */
Michal Vasko27377422018-03-15 08:59:35 +01003162 session->opts.server.ch_lock = calloc(1, sizeof *session->opts.server.ch_lock);
3163 session->opts.server.ch_cond = calloc(1, sizeof *session->opts.server.ch_cond);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003164 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
3165 ERRMEM;
3166 nc_session_free(session, NULL);
3167 return -1;
3168 }
3169 pthread_mutex_init(session->opts.server.ch_lock, NULL);
3170 pthread_cond_init(session->opts.server.ch_cond, NULL);
3171
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003172 session->flags |= NC_SESSION_CALLHOME;
3173
Michal Vasko2e6defd2016-10-07 15:48:15 +02003174 /* CH LOCK */
3175 pthread_mutex_lock(session->opts.server.ch_lock);
3176
3177 /* give the session to the user */
3178 data->session_clb(data->client_name, session);
3179
3180 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003181 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003182 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003183
Michal Vasko3f05a092018-03-13 10:39:49 +01003184 r = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
3185 if (!r) {
3186 /* we were woken up, something probably happened */
3187 if (session->status != NC_STATUS_RUNNING) {
3188 break;
3189 }
3190 } else if (r != ETIMEDOUT) {
3191 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3192 ret = -1;
3193 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003194 }
3195
Michal Vasko2e6defd2016-10-07 15:48:15 +02003196 /* check whether the client was not removed */
3197 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003198 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003199 if (!client) {
3200 /* client was removed, finish thread */
3201 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003202 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003203 ret = 1;
3204 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003205 }
3206
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003207 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003208 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003209 } else {
3210 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003211 }
3212
Michal Vasko9fb42272017-10-05 13:50:05 +02003213 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003214 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 +02003215 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3216 session->status = NC_STATUS_INVALID;
3217 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3218 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003219
3220 /* UNLOCK */
3221 nc_server_ch_client_unlock(client);
3222
3223 } while (session->status == NC_STATUS_RUNNING);
3224
Michal Vasko27377422018-03-15 08:59:35 +01003225 /* CH UNLOCK */
3226 pthread_mutex_unlock(session->opts.server.ch_lock);
3227
Michal Vasko3f05a092018-03-13 10:39:49 +01003228 if (session->status == NC_STATUS_CLOSING) {
3229 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
3230 session->flags &= ~NC_SESSION_CALLHOME;
3231 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003232
Michal Vasko3f05a092018-03-13 10:39:49 +01003233 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003234}
3235
3236static void *
3237nc_ch_client_thread(void *arg)
3238{
3239 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3240 NC_MSG_TYPE msgtype;
3241 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003242 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003243 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003244 struct nc_ch_endpt *cur_endpt;
3245 struct nc_session *session;
3246 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003247 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003248 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003249
3250 /* LOCK */
3251 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3252 if (!client) {
3253 goto cleanup;
3254 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003255 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003256
3257 cur_endpt = &client->ch_endpts[0];
3258 cur_endpt_name = strdup(cur_endpt->name);
3259
Michal Vasko29af44b2016-10-13 10:59:55 +02003260 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003261 while (1) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02003262 msgtype = nc_connect_ch_endpt(cur_endpt, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003263
3264 if (msgtype == NC_MSG_HELLO) {
3265 /* UNLOCK */
3266 nc_server_ch_client_unlock(client);
3267
Michal Vasko29af44b2016-10-13 10:59:55 +02003268 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003269 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3270 goto cleanup;
3271 }
Michal Vasko29af44b2016-10-13 10:59:55 +02003272 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003273
3274 /* LOCK */
3275 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3276 if (!client) {
3277 goto cleanup;
3278 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003279 if (client->id != client_id) {
3280 nc_server_ch_client_unlock(client);
3281 goto cleanup;
3282 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003283
3284 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003285 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003286 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003287 /* UNLOCK */
3288 nc_server_ch_client_unlock(client);
3289
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003290 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
3291 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3292 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003293
3294 /* LOCK */
3295 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3296 if (!client) {
3297 goto cleanup;
3298 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003299 if (client->id != client_id) {
3300 nc_server_ch_client_unlock(client);
3301 goto cleanup;
3302 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003303 }
3304
3305 /* set next endpoint to try */
3306 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003307 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003308 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003309 /* we keep the current one but due to unlock/lock we have to find it again */
3310 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3311 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3312 break;
3313 }
3314 }
3315 if (next_endpt_index >= client->ch_endpt_count) {
3316 /* endpoint was removed, start with the first one */
3317 next_endpt_index = 0;
3318 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003319 } else {
3320 /* just get a random index */
3321 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003322 }
3323
Michal Vasko2e6defd2016-10-07 15:48:15 +02003324 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003325 /* UNLOCK */
3326 nc_server_ch_client_unlock(client);
3327
Michal Vasko2e6defd2016-10-07 15:48:15 +02003328 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003329 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3330
Michal Vasko6bb116b2016-10-26 13:53:46 +02003331 /* LOCK */
3332 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3333 if (!client) {
3334 goto cleanup;
3335 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003336 if (client->id != client_id) {
3337 nc_server_ch_client_unlock(client);
3338 goto cleanup;
3339 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003340
Michal Vasko2e6defd2016-10-07 15:48:15 +02003341 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003342
3343 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003344 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3345 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003346 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003347 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003348 }
3349
Peter Feiged05f2252018-09-03 08:09:47 +00003350 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003351 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003352 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003353 cur_attempts = 0;
3354 } else if (cur_attempts == client->max_attempts) {
3355 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003356 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003357 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003358 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003359 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003360 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003361 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003362 }
3363
3364 cur_attempts = 0;
3365 } /* else we keep the current one */
3366 }
Peter Feiged05f2252018-09-03 08:09:47 +00003367
3368 cur_endpt = &client->ch_endpts[next_endpt_index];
3369 free(cur_endpt_name);
3370 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003371 }
3372
3373cleanup:
3374 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003375 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003376 free(data->client_name);
3377 free(data);
3378 return NULL;
3379}
3380
3381API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02003382nc_connect_ch_client_dispatch(const char *client_name, void (*session_clb)(const char *client_name,
3383 struct nc_session *new_session))
Michal Vasko3f05a092018-03-13 10:39:49 +01003384{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003385 int ret;
3386 pthread_t tid;
3387 struct nc_ch_client_thread_arg *arg;
3388
3389 if (!client_name) {
3390 ERRARG("client_name");
3391 return -1;
3392 } else if (!session_clb) {
3393 ERRARG("session_clb");
3394 return -1;
3395 }
3396
3397 arg = malloc(sizeof *arg);
3398 if (!arg) {
3399 ERRMEM;
3400 return -1;
3401 }
3402 arg->client_name = strdup(client_name);
3403 if (!arg->client_name) {
3404 ERRMEM;
3405 free(arg);
3406 return -1;
3407 }
3408 arg->session_clb = session_clb;
3409
3410 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3411 if (ret) {
3412 ERR("Creating a new thread failed (%s).", strerror(ret));
3413 free(arg->client_name);
3414 free(arg);
3415 return -1;
3416 }
3417 /* the thread now manages arg */
3418
3419 pthread_detach(tid);
3420
3421 return 0;
3422}
3423
Radek Krejci53691be2016-02-22 13:58:37 +01003424#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003425
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003426API time_t
3427nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003428{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003429 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003430 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003431 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003432 }
3433
Michal Vasko2e6defd2016-10-07 15:48:15 +02003434 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003435}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003436
3437API void
3438nc_session_set_notif_status(struct nc_session *session, int notif_status)
3439{
3440 if (!session || (session->side != NC_SERVER)) {
3441 ERRARG("session");
3442 return;
3443 }
3444
3445 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3446}
3447
3448API int
3449nc_session_get_notif_status(const struct nc_session *session)
3450{
3451 if (!session || (session->side != NC_SERVER)) {
3452 ERRARG("session");
3453 return 0;
3454 }
3455
3456 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003457}
Michal Vasko8f430592019-02-26 08:32:54 +01003458
3459API int
3460nc_session_is_callhome(const struct nc_session *session)
3461{
3462 if (!session || (session->side != NC_SERVER)) {
3463 ERRARG("session");
3464 return 0;
3465 }
3466
3467 if (session->flags & NC_SESSION_CALLHOME) {
3468 return 1;
3469 }
3470
3471 return 0;
3472}