blob: a60f34b7e82f0ae8f3afbf269abbb410facfed91 [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);
1189 if (ret == -1) {
1190 ERR("Session %u: failed to write reply.", session->id);
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);
1249 if (ret == NC_MSG_ERROR) {
Radek Krejci93e80222016-10-03 13:34:25 +02001250 ERR("Session %u: failed to write notification.", session->id);
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 Vasko71090fc2016-05-24 16:37:28 +02001317 ERR("Session %u: failed to write reply.", session->id);
1318 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{
1723 const struct passwd *pw;
1724 struct ucred ucred;
1725 char *username;
1726 socklen_t len;
1727
1728 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;
1757}
1758
Michal Vaskoe2713da2016-08-22 16:06:40 +02001759API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001760nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001761{
Michal Vasko3031aae2016-01-27 16:07:18 +01001762 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001763 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001764
Michal Vasko45e53ae2016-04-07 11:46:03 +02001765 if (!name) {
1766 ERRARG("name");
1767 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001768 }
1769
Michal Vaskoade892d2017-02-22 13:40:35 +01001770 /* BIND LOCK */
1771 pthread_mutex_lock(&server_opts.bind_lock);
1772
1773 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001774 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001775
1776 /* check name uniqueness */
1777 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001778 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001779 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001780 ret = -1;
1781 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001782 }
1783 }
1784
Michal Vasko3031aae2016-01-27 16:07:18 +01001785 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001786 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001787 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001788 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001789 ret = -1;
1790 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001791 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001792 memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001793 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001794 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001795 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1796 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1797 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001798
Michal Vaskoe2713da2016-08-22 16:06:40 +02001799 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001800 if (!server_opts.binds) {
1801 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001802 ret = -1;
1803 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001804 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001805
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001806 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001807 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1808
1809 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001810#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001811 case NC_TI_LIBSSH:
1812 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1813 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1814 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001815 ret = -1;
1816 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001817 }
1818 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1819 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1820 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02001821 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001822 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001823#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001824#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001825 case NC_TI_OPENSSL:
1826 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1827 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1828 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001829 ret = -1;
1830 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001831 }
1832 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001833#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001834 case NC_TI_UNIX:
1835 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1836 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1837 ERRMEM;
1838 ret = -1;
1839 goto cleanup;
1840 }
1841 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1842 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1843 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1844 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001845 default:
1846 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001847 ret = -1;
1848 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001849 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001850
Michal Vaskoade892d2017-02-22 13:40:35 +01001851cleanup:
1852 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001853 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001854
Michal Vaskoade892d2017-02-22 13:40:35 +01001855 /* BIND UNLOCK */
1856 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001857
Michal Vaskoade892d2017-02-22 13:40:35 +01001858 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001859}
1860
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001861API int
Michal Vasko59050372016-11-22 14:33:55 +01001862nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001863{
1864 uint32_t i;
1865 int ret = -1;
1866
Michal Vaskoade892d2017-02-22 13:40:35 +01001867 /* BIND LOCK */
1868 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001869
Michal Vaskoade892d2017-02-22 13:40:35 +01001870 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001871 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001872
Michal Vasko59050372016-11-22 14:33:55 +01001873 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001874 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001875 for (i = 0; i < server_opts.endpt_count; ++i) {
1876 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001877 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001878#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001879 case NC_TI_LIBSSH:
1880 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1881 free(server_opts.endpts[i].opts.ssh);
1882 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001883#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001884#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001885 case NC_TI_OPENSSL:
1886 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1887 free(server_opts.endpts[i].opts.tls);
1888 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001889#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001890 case NC_TI_UNIX:
1891 free(server_opts.endpts[i].opts.unixsock);
1892 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001893 default:
1894 ERRINT;
1895 /* won't get here ...*/
1896 break;
1897 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001898 ret = 0;
1899 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001900 free(server_opts.endpts);
1901 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001902
1903 /* remove all binds */
1904 for (i = 0; i < server_opts.endpt_count; ++i) {
1905 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1906 if (server_opts.binds[i].sock > -1) {
1907 close(server_opts.binds[i].sock);
1908 }
1909 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001910 free(server_opts.binds);
1911 server_opts.binds = NULL;
1912
Michal Vasko3031aae2016-01-27 16:07:18 +01001913 server_opts.endpt_count = 0;
1914
Michal Vasko1a38c862016-01-15 15:50:07 +01001915 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001916 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001917 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001918 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001919 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001920 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001921 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001922#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001923 case NC_TI_LIBSSH:
1924 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1925 free(server_opts.endpts[i].opts.ssh);
1926 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001927#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001928#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001929 case NC_TI_OPENSSL:
1930 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1931 free(server_opts.endpts[i].opts.tls);
1932 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001933#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001934 case NC_TI_UNIX:
1935 free(server_opts.endpts[i].opts.unixsock);
1936 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001937 default:
1938 ERRINT;
1939 break;
1940 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001941
Michal Vaskoe2713da2016-08-22 16:06:40 +02001942 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001943 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1944 if (server_opts.binds[i].sock > -1) {
1945 close(server_opts.binds[i].sock);
1946 }
1947
1948 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001949 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001950 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001951 free(server_opts.binds);
1952 server_opts.binds = NULL;
1953 free(server_opts.endpts);
1954 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001955 } else if (i < server_opts.endpt_count) {
1956 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1957 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001958 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001959
1960 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001961 if (name) {
1962 break;
1963 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001964 }
1965 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001966 }
1967
Michal Vaskoade892d2017-02-22 13:40:35 +01001968 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001969 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001970
Michal Vaskoade892d2017-02-22 13:40:35 +01001971 /* BIND UNLOCK */
1972 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001973
1974 return ret;
1975}
1976
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001977API int
1978nc_server_endpt_count(void)
1979{
1980 return server_opts.endpt_count;
1981}
1982
1983int
1984nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
1985{
1986 struct nc_endpt *endpt;
1987 struct nc_bind *bind = NULL;
1988 uint16_t i;
1989 int sock = -1, set_addr, ret = 0;
1990
1991 if (!endpt_name) {
1992 ERRARG("endpt_name");
1993 return -1;
1994 } else if ((!address && !port) || (address && port)) {
1995 ERRARG("address and port");
1996 return -1;
1997 }
1998
1999 if (address) {
2000 set_addr = 1;
2001 } else {
2002 set_addr = 0;
2003 }
2004
2005 /* BIND LOCK */
2006 pthread_mutex_lock(&server_opts.bind_lock);
2007
2008 /* ENDPT LOCK */
2009 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2010 if (!endpt) {
2011 /* BIND UNLOCK */
2012 pthread_mutex_unlock(&server_opts.bind_lock);
2013 return -1;
2014 }
2015
2016 bind = &server_opts.binds[i];
2017
2018 if (set_addr) {
2019 port = bind->port;
2020 } else {
2021 address = bind->address;
2022 }
2023
2024 if (!set_addr && endpt->ti == NC_TI_UNIX) {
2025 ret = -1;
2026 goto cleanup;
2027 }
2028
2029 /* we have all the information we need to create a listening socket */
2030 if (address && (port || endpt->ti == NC_TI_UNIX)) {
2031 /* create new socket, close the old one */
2032 if (endpt->ti == NC_TI_UNIX)
2033 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
2034 else
2035 sock = nc_sock_listen_inet(address, port, &endpt->ka);
2036 if (sock == -1) {
2037 ret = -1;
2038 goto cleanup;
2039 }
2040
2041 if (bind->sock > -1) {
2042 close(bind->sock);
2043 }
2044 bind->sock = sock;
2045 } /* else we are just setting address or port */
2046
2047 if (set_addr) {
2048 lydict_remove(server_opts.ctx, bind->address);
2049 bind->address = lydict_insert(server_opts.ctx, address, 0);
2050 } else {
2051 bind->port = port;
2052 }
2053
2054 if (sock > -1) {
2055#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
2056 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
2057#elif defined(NC_ENABLED_SSH)
2058 VRB("Listening on %s:%u for SSH connections.", address, port);
2059#else
2060 VRB("Listening on %s:%u for TLS connections.", address, port);
2061#endif
2062 }
2063
2064cleanup:
2065 /* ENDPT UNLOCK */
2066 pthread_rwlock_unlock(&server_opts.endpt_lock);
2067
2068 /* BIND UNLOCK */
2069 pthread_mutex_unlock(&server_opts.bind_lock);
2070
2071 return ret;
2072}
2073
2074API int
2075nc_server_endpt_set_address(const char *endpt_name, const char *address)
2076{
2077 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2078}
2079
2080API int
2081nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2082{
2083 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2084}
2085
2086API int
2087nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2088{
2089 struct nc_endpt *endpt;
2090 uint16_t i;
2091 int ret = 0;
2092
2093 if (!endpt_name) {
2094 ERRARG("endpt_name");
2095 return -1;
2096 } else if (mode == 0) {
2097 ERRARG("mode");
2098 return -1;
2099 }
2100
2101 /* ENDPT LOCK */
2102 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2103 if (!endpt)
2104 return -1;
2105
2106 if (endpt->ti != NC_TI_UNIX) {
2107 ret = -1;
2108 goto cleanup;
2109 }
2110
2111 endpt->opts.unixsock->mode = mode;
2112 endpt->opts.unixsock->uid = uid;
2113 endpt->opts.unixsock->gid = gid;
2114
2115cleanup:
2116 /* ENDPT UNLOCK */
2117 pthread_rwlock_unlock(&server_opts.endpt_lock);
2118
2119 return ret;
2120}
2121
2122API int
2123nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2124{
2125 struct nc_endpt *endpt;
2126 int ret = 0;
2127
2128 if (!endpt_name) {
2129 ERRARG("endpt_name");
2130 return -1;
2131 }
2132
2133 /* ENDPT LOCK */
2134 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2135 if (!endpt) {
2136 return -1;
2137 }
2138
2139 endpt->ka.enabled = (enable ? 1 : 0);
2140
2141 /* ENDPT UNLOCK */
2142 pthread_rwlock_unlock(&server_opts.endpt_lock);
2143
2144 return ret;
2145}
2146
2147API int
2148nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2149{
2150 struct nc_endpt *endpt;
2151 int ret = 0;
2152
2153 if (!endpt_name) {
2154 ERRARG("endpt_name");
2155 return -1;
2156 }
2157
2158 /* ENDPT LOCK */
2159 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2160 if (!endpt) {
2161 return -1;
2162 }
2163
2164 if (idle_time > -1) {
2165 endpt->ka.idle_time = idle_time;
2166 }
2167 if (max_probes > -1) {
2168 endpt->ka.max_probes = max_probes;
2169 }
2170 if (probe_interval > -1) {
2171 endpt->ka.probe_interval = probe_interval;
2172 }
2173
2174 /* ENDPT UNLOCK */
2175 pthread_rwlock_unlock(&server_opts.endpt_lock);
2176
2177 return ret;
2178}
2179
Michal Vasko71090fc2016-05-24 16:37:28 +02002180API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002181nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002182{
Michal Vasko71090fc2016-05-24 16:37:28 +02002183 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002184 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002185 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002186 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002187 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002188
Michal Vasko45e53ae2016-04-07 11:46:03 +02002189 if (!server_opts.ctx) {
2190 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002191 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002192 } else if (!session) {
2193 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002194 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002195 }
2196
Michal Vaskoade892d2017-02-22 13:40:35 +01002197 /* BIND LOCK */
2198 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002199
2200 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002201 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002202 /* BIND UNLOCK */
2203 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002204 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002205 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002206
Michal Vaskoe2713da2016-08-22 16:06:40 +02002207 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002208 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002209 /* BIND UNLOCK */
2210 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002211 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002212 if (!ret) {
2213 return NC_MSG_WOULDBLOCK;
2214 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002215 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002216 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002217
2218 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2219 /* ENDPT READ LOCK */
2220 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2221
2222 /* BIND UNLOCK */
2223 pthread_mutex_unlock(&server_opts.bind_lock);
2224
Michal Vaskob48aa812016-01-18 14:13:09 +01002225 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002226
Michal Vasko131120a2018-05-29 15:44:02 +02002227 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002228 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002229 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002230 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002231 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002232 msgtype = NC_MSG_ERROR;
2233 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002234 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002235 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002236 (*session)->ctx = server_opts.ctx;
2237 (*session)->flags = NC_SESSION_SHAREDCTX;
2238 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
2239 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002240
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002241 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002242#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002243 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2244 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002245 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002246 if (ret < 0) {
2247 msgtype = NC_MSG_ERROR;
2248 goto cleanup;
2249 } else if (!ret) {
2250 msgtype = NC_MSG_WOULDBLOCK;
2251 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002252 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002253 } else
2254#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002255#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002256 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2257 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002258 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002259 if (ret < 0) {
2260 msgtype = NC_MSG_ERROR;
2261 goto cleanup;
2262 } else if (!ret) {
2263 msgtype = NC_MSG_WOULDBLOCK;
2264 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002265 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002266 } else
2267#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002268 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2269 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2270 ret = nc_accept_unix(*session, sock);
2271 if (ret < 0) {
2272 msgtype = NC_MSG_ERROR;
2273 goto cleanup;
2274 }
2275 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002276 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002277 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002278 msgtype = NC_MSG_ERROR;
2279 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002280 }
2281
Michal Vasko2cc4c682016-03-01 09:16:48 +01002282 (*session)->data = NULL;
2283
Michal Vaskoade892d2017-02-22 13:40:35 +01002284 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002285 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002286
Michal Vaskob48aa812016-01-18 14:13:09 +01002287 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002288 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002289
Michal Vasko9e036d52016-01-08 10:49:26 +01002290 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002291 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002292 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002293 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002294 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002295 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002296 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002297
2298 nc_gettimespec_mono(&ts_cur);
2299 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2300 nc_gettimespec_real(&ts_cur);
2301 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002302 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002303
Michal Vasko71090fc2016-05-24 16:37:28 +02002304 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002305
Michal Vasko71090fc2016-05-24 16:37:28 +02002306cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002307 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002308 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002309
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002310 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002311 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002312 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002313}
2314
Michal Vaskoadf30f02019-06-24 09:34:47 +02002315/* client is expected to be locked */
2316static int
2317_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 +02002318{
2319 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002320 int ret = -1;
2321
2322 if (!endpt_name) {
2323 /* remove all endpoints */
2324 for (i = 0; i < client->ch_endpt_count; ++i) {
2325 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2326 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2327 if (client->ch_endpts[i].sock_pending != -1) {
2328 close(client->ch_endpts[i].sock_pending);
2329 }
2330 switch (client->ch_endpts[i].ti) {
2331#ifdef NC_ENABLED_SSH
2332 case NC_TI_LIBSSH:
2333 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2334 free(client->ch_endpts[i].opts.ssh);
2335 break;
2336#endif
2337#ifdef NC_ENABLED_TLS
2338 case NC_TI_OPENSSL:
2339 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2340 free(client->ch_endpts[i].opts.tls);
2341 break;
2342#endif
2343 default:
2344 ERRINT;
2345 /* won't get here ...*/
2346 break;
2347 }
2348 }
2349 free(client->ch_endpts);
2350 client->ch_endpts = NULL;
2351 client->ch_endpt_count = 0;
2352
2353 ret = 0;
2354 } else {
2355 for (i = 0; i < client->ch_endpt_count; ++i) {
2356 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
2357 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2358 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2359 if (client->ch_endpts[i].sock_pending != -1) {
2360 close(client->ch_endpts[i].sock_pending);
2361 }
2362 switch (client->ch_endpts[i].ti) {
2363#ifdef NC_ENABLED_SSH
2364 case NC_TI_LIBSSH:
2365 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2366 free(client->ch_endpts[i].opts.ssh);
2367 break;
2368#endif
2369#ifdef NC_ENABLED_TLS
2370 case NC_TI_OPENSSL:
2371 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2372 free(client->ch_endpts[i].opts.tls);
2373 break;
2374#endif
2375 default:
2376 ERRINT;
2377 /* won't get here ...*/
2378 break;
2379 }
2380
2381 /* move last endpoint to the empty space */
2382 --client->ch_endpt_count;
2383 if (i < client->ch_endpt_count) {
2384 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2385 } else if (!server_opts.ch_client_count) {
2386 free(server_opts.ch_clients);
2387 server_opts.ch_clients = NULL;
2388 }
2389
2390 ret = 0;
2391 break;
2392 }
2393 }
2394 }
2395
2396 return ret;
2397}
2398
2399API int
2400nc_server_ch_add_client(const char *name)
2401{
2402 uint16_t i;
2403 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002404
2405 if (!name) {
2406 ERRARG("name");
2407 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002408 }
2409
2410 /* WRITE LOCK */
2411 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2412
2413 /* check name uniqueness */
2414 for (i = 0; i < server_opts.ch_client_count; ++i) {
2415 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2416 ERR("Call Home client \"%s\" already exists.", name);
2417 /* WRITE UNLOCK */
2418 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2419 return -1;
2420 }
2421 }
2422
2423 ++server_opts.ch_client_count;
2424 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2425 if (!server_opts.ch_clients) {
2426 ERRMEM;
2427 /* WRITE UNLOCK */
2428 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2429 return -1;
2430 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002431 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002432
Michal Vaskoadf30f02019-06-24 09:34:47 +02002433 client->name = lydict_insert(server_opts.ctx, name, 0);
2434 client->id = ATOMIC_INC(&server_opts.new_client_id);
2435 client->ch_endpts = NULL;
2436 client->ch_endpt_count = 0;
2437 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002438
Michal Vasko2e6defd2016-10-07 15:48:15 +02002439 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002440 client->start_with = NC_CH_FIRST_LISTED;
2441 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002442
Michal Vaskoadf30f02019-06-24 09:34:47 +02002443 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002444
2445 /* WRITE UNLOCK */
2446 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2447
2448 return 0;
2449}
2450
2451API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002452nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002453{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002454 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002455 int ret = -1;
2456
2457 /* WRITE LOCK */
2458 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2459
Michal Vaskoadf30f02019-06-24 09:34:47 +02002460 if (!name) {
2461 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002462 for (i = 0; i < server_opts.ch_client_count; ++i) {
2463 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2464
2465 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002466 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002467
2468 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002469 ret = 0;
2470 }
2471 free(server_opts.ch_clients);
2472 server_opts.ch_clients = NULL;
2473
2474 server_opts.ch_client_count = 0;
2475
2476 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002477 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002478 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002479 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002480 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2481
Michal Vasko2e6defd2016-10-07 15:48:15 +02002482 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002483 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002484
2485 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2486
2487 /* move last client and endpoint(s) to the empty space */
2488 --server_opts.ch_client_count;
2489 if (i < server_opts.ch_client_count) {
2490 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002491 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002492 } else if (!server_opts.ch_client_count) {
2493 free(server_opts.ch_clients);
2494 server_opts.ch_clients = NULL;
2495 }
2496
2497 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002498 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002499 }
2500 }
2501 }
2502
2503 /* WRITE UNLOCK */
2504 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2505
2506 return ret;
2507}
2508
2509API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002510nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002511{
2512 uint16_t i;
2513 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002514 struct nc_ch_endpt *endpt;
2515 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002516
2517 if (!client_name) {
2518 ERRARG("client_name");
2519 return -1;
2520 } else if (!endpt_name) {
2521 ERRARG("endpt_name");
2522 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002523 } else if (!ti) {
2524 ERRARG("ti");
2525 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002526 }
2527
2528 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002529 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002530 if (!client) {
2531 return -1;
2532 }
2533
2534 for (i = 0; i < client->ch_endpt_count; ++i) {
2535 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2536 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002537 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002538 }
2539 }
2540
2541 ++client->ch_endpt_count;
2542 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2543 if (!client->ch_endpts) {
2544 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002545 goto cleanup;
2546 }
2547 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2548
2549 memset(endpt, 0, sizeof *client->ch_endpts);
2550 endpt->name = lydict_insert(server_opts.ctx, endpt_name, 0);
2551 endpt->ti = ti;
2552 endpt->sock_pending = -1;
2553 endpt->ka.idle_time = 1;
2554 endpt->ka.max_probes = 10;
2555 endpt->ka.probe_interval = 5;
2556
2557 switch (ti) {
2558#ifdef NC_ENABLED_SSH
2559 case NC_TI_LIBSSH:
2560 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2561 if (!endpt->opts.ssh) {
2562 ERRMEM;
2563 goto cleanup;
2564 }
2565 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2566 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002567 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002568 break;
2569#endif
2570#ifdef NC_ENABLED_TLS
2571 case NC_TI_OPENSSL:
2572 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2573 if (!endpt->opts.tls) {
2574 ERRMEM;
2575 goto cleanup;
2576 }
2577 break;
2578#endif
2579 default:
2580 ERRINT;
2581 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002582 }
2583
Michal Vaskoadf30f02019-06-24 09:34:47 +02002584 /* success */
2585 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002586
Michal Vaskoadf30f02019-06-24 09:34:47 +02002587cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002588 /* UNLOCK */
2589 nc_server_ch_client_unlock(client);
2590
Michal Vaskoadf30f02019-06-24 09:34:47 +02002591 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002592}
2593
2594API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002595nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002596{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002597 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002598 struct nc_ch_client *client;
2599
2600 if (!client_name) {
2601 ERRARG("client_name");
2602 return -1;
2603 }
2604
2605 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002606 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002607 if (!client) {
2608 return -1;
2609 }
2610
Michal Vaskoadf30f02019-06-24 09:34:47 +02002611 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002612
2613 /* UNLOCK */
2614 nc_server_ch_client_unlock(client);
2615
2616 return ret;
2617}
2618
2619API int
2620nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2621{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002622 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002623 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002624
2625 if (!client_name) {
2626 ERRARG("client_name");
2627 return -1;
2628 } else if (!endpt_name) {
2629 ERRARG("endpt_name");
2630 return -1;
2631 } else if (!address) {
2632 ERRARG("address");
2633 return -1;
2634 }
2635
2636 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002637 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2638 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002639 return -1;
2640 }
2641
Michal Vaskoadf30f02019-06-24 09:34:47 +02002642 lydict_remove(server_opts.ctx, endpt->address);
2643 endpt->address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002644
2645 /* UNLOCK */
2646 nc_server_ch_client_unlock(client);
2647
Michal Vaskoadf30f02019-06-24 09:34:47 +02002648 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002649}
2650
2651API int
2652nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2653{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002654 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002655 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002656
2657 if (!client_name) {
2658 ERRARG("client_name");
2659 return -1;
2660 } else if (!endpt_name) {
2661 ERRARG("endpt_name");
2662 return -1;
2663 } else if (!port) {
2664 ERRARG("port");
2665 return -1;
2666 }
2667
2668 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002669 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2670 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002671 return -1;
2672 }
2673
Michal Vaskoadf30f02019-06-24 09:34:47 +02002674 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002675
2676 /* UNLOCK */
2677 nc_server_ch_client_unlock(client);
2678
Michal Vaskoadf30f02019-06-24 09:34:47 +02002679 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002680}
2681
2682API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002683nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2684{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002685 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002686 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002687
2688 if (!client_name) {
2689 ERRARG("client_name");
2690 return -1;
2691 } else if (!endpt_name) {
2692 ERRARG("endpt_name");
2693 return -1;
2694 }
2695
2696 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002697 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2698 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002699 return -1;
2700 }
2701
Michal Vaskoadf30f02019-06-24 09:34:47 +02002702 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002703
2704 /* UNLOCK */
2705 nc_server_ch_client_unlock(client);
2706
Michal Vasko9af829a2019-09-12 13:50:00 +02002707 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002708}
2709
2710API int
2711nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
2712 int probe_interval)
2713{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002714 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002715 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002716
2717 if (!client_name) {
2718 ERRARG("client_name");
2719 return -1;
2720 } else if (!endpt_name) {
2721 ERRARG("endpt_name");
2722 return -1;
2723 }
2724
2725 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002726 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2727 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002728 return -1;
2729 }
2730
Michal Vaskoadf30f02019-06-24 09:34:47 +02002731 if (idle_time > -1) {
2732 endpt->ka.idle_time = idle_time;
2733 }
2734 if (max_probes > -1) {
2735 endpt->ka.max_probes = max_probes;
2736 }
2737 if (probe_interval > -1) {
2738 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002739 }
2740
2741 /* UNLOCK */
2742 nc_server_ch_client_unlock(client);
2743
Michal Vasko9af829a2019-09-12 13:50:00 +02002744 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002745}
2746
2747API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002748nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2749{
2750 struct nc_ch_client *client;
2751
2752 if (!client_name) {
2753 ERRARG("client_name");
2754 return -1;
2755 } else if (!conn_type) {
2756 ERRARG("conn_type");
2757 return -1;
2758 }
2759
2760 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002761 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002762 if (!client) {
2763 return -1;
2764 }
2765
2766 if (client->conn_type != conn_type) {
2767 client->conn_type = conn_type;
2768
2769 /* set default options */
2770 switch (conn_type) {
2771 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002772 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002773 break;
2774 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002775 client->conn.period.period = 60;
2776 client->conn.period.anchor_time = 0;
2777 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002778 break;
2779 default:
2780 ERRINT;
2781 break;
2782 }
2783 }
2784
2785 /* UNLOCK */
2786 nc_server_ch_client_unlock(client);
2787
2788 return 0;
2789}
2790
2791API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002792nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
2793{
2794 struct nc_ch_client *client;
2795
2796 if (!client_name) {
2797 ERRARG("client_name");
2798 return -1;
2799 } else if (!period) {
2800 ERRARG("period");
2801 return -1;
2802 }
2803
2804 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002805 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002806 if (!client) {
2807 return -1;
2808 }
2809
2810 if (client->conn_type != NC_CH_PERIOD) {
2811 ERR("Call Home client \"%s\" is not of periodic connection type.");
2812 /* UNLOCK */
2813 nc_server_ch_client_unlock(client);
2814 return -1;
2815 }
2816
2817 client->conn.period.period = period;
2818
2819 /* UNLOCK */
2820 nc_server_ch_client_unlock(client);
2821
2822 return 0;
2823}
2824
2825API int
2826nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002827{
2828 struct nc_ch_client *client;
2829
2830 if (!client_name) {
2831 ERRARG("client_name");
2832 return -1;
2833 }
2834
2835 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002836 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002837 if (!client) {
2838 return -1;
2839 }
2840
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002841 if (client->conn_type != NC_CH_PERIOD) {
2842 ERR("Call Home client \"%s\" is not of periodic connection type.");
Michal Vasko2e6defd2016-10-07 15:48:15 +02002843 /* UNLOCK */
2844 nc_server_ch_client_unlock(client);
2845 return -1;
2846 }
2847
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002848 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002849
2850 /* UNLOCK */
2851 nc_server_ch_client_unlock(client);
2852
2853 return 0;
2854}
2855
2856API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002857nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002858{
2859 struct nc_ch_client *client;
2860
2861 if (!client_name) {
2862 ERRARG("client_name");
2863 return -1;
2864 }
2865
2866 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002867 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002868 if (!client) {
2869 return -1;
2870 }
2871
2872 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002873 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002874 /* UNLOCK */
2875 nc_server_ch_client_unlock(client);
2876 return -1;
2877 }
2878
2879 client->conn.period.idle_timeout = idle_timeout;
2880
2881 /* UNLOCK */
2882 nc_server_ch_client_unlock(client);
2883
2884 return 0;
2885}
2886
2887API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002888nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2889{
2890 struct nc_ch_client *client;
2891
2892 if (!client_name) {
2893 ERRARG("client_name");
2894 return -1;
2895 }
2896
2897 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002898 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002899 if (!client) {
2900 return -1;
2901 }
2902
2903 client->start_with = start_with;
2904
2905 /* UNLOCK */
2906 nc_server_ch_client_unlock(client);
2907
2908 return 0;
2909}
2910
2911API int
2912nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2913{
2914 struct nc_ch_client *client;
2915
2916 if (!client_name) {
2917 ERRARG("client_name");
2918 return -1;
2919 } else if (!max_attempts) {
2920 ERRARG("max_attempts");
2921 return -1;
2922 }
2923
2924 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002925 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002926 if (!client) {
2927 return -1;
2928 }
2929
2930 client->max_attempts = max_attempts;
2931
2932 /* UNLOCK */
2933 nc_server_ch_client_unlock(client);
2934
2935 return 0;
2936}
2937
2938/* client lock is expected to be held */
2939static NC_MSG_TYPE
Michal Vaskoadf30f02019-06-24 09:34:47 +02002940nc_connect_ch_endpt(struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01002941{
Michal Vasko71090fc2016-05-24 16:37:28 +02002942 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002943 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002944 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01002945 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01002946
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002947 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->ka, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002948 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002949 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002950 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00002951 /* no need to store the socket as pending any longer */
2952 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01002953
Michal Vasko131120a2018-05-29 15:44:02 +02002954 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002955 if (!(*session)) {
2956 ERRMEM;
2957 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01002958 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002959 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002960 }
2961 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01002962 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002963 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko66032bc2019-01-22 15:03:12 +01002964 (*session)->host = lydict_insert_zc(server_opts.ctx, ip_host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002965 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002966
Michal Vaskob05053d2016-01-22 16:12:06 +01002967 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002968#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02002969 if (endpt->ti == NC_TI_LIBSSH) {
2970 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002971 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002972 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002973
Michal Vasko71090fc2016-05-24 16:37:28 +02002974 if (ret < 0) {
2975 msgtype = NC_MSG_ERROR;
2976 goto fail;
2977 } else if (!ret) {
2978 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002979 goto fail;
2980 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002981 } else
2982#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002983#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02002984 if (endpt->ti == NC_TI_OPENSSL) {
2985 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002986 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002987 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002988
Michal Vasko71090fc2016-05-24 16:37:28 +02002989 if (ret < 0) {
2990 msgtype = NC_MSG_ERROR;
2991 goto fail;
2992 } else if (!ret) {
2993 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002994 goto fail;
2995 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002996 } else
2997#endif
2998 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002999 ERRINT;
3000 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003001 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003002 goto fail;
3003 }
3004
3005 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01003006 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003007
3008 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003009 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003010 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003011 goto fail;
3012 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003013
3014 nc_gettimespec_mono(&ts_cur);
3015 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3016 nc_gettimespec_real(&ts_cur);
3017 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003018 (*session)->status = NC_STATUS_RUNNING;
3019
Michal Vasko71090fc2016-05-24 16:37:28 +02003020 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003021
3022fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003023 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003024 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003025 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003026}
3027
Michal Vasko2e6defd2016-10-07 15:48:15 +02003028struct nc_ch_client_thread_arg {
3029 char *client_name;
3030 void (*session_clb)(const char *client_name, struct nc_session *new_session);
3031};
3032
3033static struct nc_ch_client *
3034nc_server_ch_client_with_endpt_lock(const char *name)
3035{
3036 struct nc_ch_client *client;
3037
3038 while (1) {
3039 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003040 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003041 if (!client) {
3042 return NULL;
3043 }
3044 if (client->ch_endpt_count) {
3045 return client;
3046 }
3047 /* no endpoints defined yet */
3048
3049 /* UNLOCK */
3050 nc_server_ch_client_unlock(client);
3051
3052 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3053 }
3054
3055 return NULL;
3056}
3057
3058static int
3059nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3060{
Michal Vasko3f05a092018-03-13 10:39:49 +01003061 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003062 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003063 struct timespec ts;
3064 struct nc_ch_client *client;
3065
3066 /* session created, initialize condition */
Michal Vasko27377422018-03-15 08:59:35 +01003067 session->opts.server.ch_lock = calloc(1, sizeof *session->opts.server.ch_lock);
3068 session->opts.server.ch_cond = calloc(1, sizeof *session->opts.server.ch_cond);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003069 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
3070 ERRMEM;
3071 nc_session_free(session, NULL);
3072 return -1;
3073 }
3074 pthread_mutex_init(session->opts.server.ch_lock, NULL);
3075 pthread_cond_init(session->opts.server.ch_cond, NULL);
3076
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003077 session->flags |= NC_SESSION_CALLHOME;
3078
Michal Vasko2e6defd2016-10-07 15:48:15 +02003079 /* CH LOCK */
3080 pthread_mutex_lock(session->opts.server.ch_lock);
3081
3082 /* give the session to the user */
3083 data->session_clb(data->client_name, session);
3084
3085 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003086 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003087 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003088
Michal Vasko3f05a092018-03-13 10:39:49 +01003089 r = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
3090 if (!r) {
3091 /* we were woken up, something probably happened */
3092 if (session->status != NC_STATUS_RUNNING) {
3093 break;
3094 }
3095 } else if (r != ETIMEDOUT) {
3096 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3097 ret = -1;
3098 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003099 }
3100
Michal Vasko2e6defd2016-10-07 15:48:15 +02003101 /* check whether the client was not removed */
3102 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003103 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003104 if (!client) {
3105 /* client was removed, finish thread */
3106 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003107 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003108 ret = 1;
3109 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003110 }
3111
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003112 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003113 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003114 } else {
3115 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003116 }
3117
Michal Vasko9fb42272017-10-05 13:50:05 +02003118 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003119 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 +02003120 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3121 session->status = NC_STATUS_INVALID;
3122 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3123 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003124
3125 /* UNLOCK */
3126 nc_server_ch_client_unlock(client);
3127
3128 } while (session->status == NC_STATUS_RUNNING);
3129
Michal Vasko27377422018-03-15 08:59:35 +01003130 /* CH UNLOCK */
3131 pthread_mutex_unlock(session->opts.server.ch_lock);
3132
Michal Vasko3f05a092018-03-13 10:39:49 +01003133 if (session->status == NC_STATUS_CLOSING) {
3134 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
3135 session->flags &= ~NC_SESSION_CALLHOME;
3136 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003137
Michal Vasko3f05a092018-03-13 10:39:49 +01003138 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003139}
3140
3141static void *
3142nc_ch_client_thread(void *arg)
3143{
3144 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3145 NC_MSG_TYPE msgtype;
3146 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003147 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003148 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003149 struct nc_ch_endpt *cur_endpt;
3150 struct nc_session *session;
3151 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003152 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003153 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003154
3155 /* LOCK */
3156 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3157 if (!client) {
3158 goto cleanup;
3159 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003160 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003161
3162 cur_endpt = &client->ch_endpts[0];
3163 cur_endpt_name = strdup(cur_endpt->name);
3164
Michal Vasko29af44b2016-10-13 10:59:55 +02003165 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003166 while (1) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02003167 msgtype = nc_connect_ch_endpt(cur_endpt, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003168
3169 if (msgtype == NC_MSG_HELLO) {
3170 /* UNLOCK */
3171 nc_server_ch_client_unlock(client);
3172
Michal Vasko29af44b2016-10-13 10:59:55 +02003173 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003174 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3175 goto cleanup;
3176 }
Michal Vasko29af44b2016-10-13 10:59:55 +02003177 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003178
3179 /* LOCK */
3180 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3181 if (!client) {
3182 goto cleanup;
3183 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003184 if (client->id != client_id) {
3185 nc_server_ch_client_unlock(client);
3186 goto cleanup;
3187 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003188
3189 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003190 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003191 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003192 /* UNLOCK */
3193 nc_server_ch_client_unlock(client);
3194
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003195 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
3196 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3197 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003198
3199 /* LOCK */
3200 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3201 if (!client) {
3202 goto cleanup;
3203 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003204 if (client->id != client_id) {
3205 nc_server_ch_client_unlock(client);
3206 goto cleanup;
3207 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003208 }
3209
3210 /* set next endpoint to try */
3211 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003212 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003213 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003214 /* we keep the current one but due to unlock/lock we have to find it again */
3215 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3216 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3217 break;
3218 }
3219 }
3220 if (next_endpt_index >= client->ch_endpt_count) {
3221 /* endpoint was removed, start with the first one */
3222 next_endpt_index = 0;
3223 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003224 } else {
3225 /* just get a random index */
3226 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003227 }
3228
Michal Vasko2e6defd2016-10-07 15:48:15 +02003229 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003230 /* UNLOCK */
3231 nc_server_ch_client_unlock(client);
3232
Michal Vasko2e6defd2016-10-07 15:48:15 +02003233 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003234 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3235
Michal Vasko6bb116b2016-10-26 13:53:46 +02003236 /* LOCK */
3237 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3238 if (!client) {
3239 goto cleanup;
3240 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003241 if (client->id != client_id) {
3242 nc_server_ch_client_unlock(client);
3243 goto cleanup;
3244 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003245
Michal Vasko2e6defd2016-10-07 15:48:15 +02003246 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003247
3248 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003249 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3250 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003251 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003252 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003253 }
3254
Peter Feiged05f2252018-09-03 08:09:47 +00003255 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003256 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003257 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003258 cur_attempts = 0;
3259 } else if (cur_attempts == client->max_attempts) {
3260 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003261 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003262 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003263 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003264 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003265 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003266 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003267 }
3268
3269 cur_attempts = 0;
3270 } /* else we keep the current one */
3271 }
Peter Feiged05f2252018-09-03 08:09:47 +00003272
3273 cur_endpt = &client->ch_endpts[next_endpt_index];
3274 free(cur_endpt_name);
3275 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003276 }
3277
3278cleanup:
3279 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003280 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003281 free(data->client_name);
3282 free(data);
3283 return NULL;
3284}
3285
3286API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02003287nc_connect_ch_client_dispatch(const char *client_name, void (*session_clb)(const char *client_name,
3288 struct nc_session *new_session))
Michal Vasko3f05a092018-03-13 10:39:49 +01003289{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003290 int ret;
3291 pthread_t tid;
3292 struct nc_ch_client_thread_arg *arg;
3293
3294 if (!client_name) {
3295 ERRARG("client_name");
3296 return -1;
3297 } else if (!session_clb) {
3298 ERRARG("session_clb");
3299 return -1;
3300 }
3301
3302 arg = malloc(sizeof *arg);
3303 if (!arg) {
3304 ERRMEM;
3305 return -1;
3306 }
3307 arg->client_name = strdup(client_name);
3308 if (!arg->client_name) {
3309 ERRMEM;
3310 free(arg);
3311 return -1;
3312 }
3313 arg->session_clb = session_clb;
3314
3315 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3316 if (ret) {
3317 ERR("Creating a new thread failed (%s).", strerror(ret));
3318 free(arg->client_name);
3319 free(arg);
3320 return -1;
3321 }
3322 /* the thread now manages arg */
3323
3324 pthread_detach(tid);
3325
3326 return 0;
3327}
3328
Radek Krejci53691be2016-02-22 13:58:37 +01003329#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003330
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003331API time_t
3332nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003333{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003334 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003335 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003336 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003337 }
3338
Michal Vasko2e6defd2016-10-07 15:48:15 +02003339 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003340}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003341
3342API void
3343nc_session_set_notif_status(struct nc_session *session, int notif_status)
3344{
3345 if (!session || (session->side != NC_SERVER)) {
3346 ERRARG("session");
3347 return;
3348 }
3349
3350 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3351}
3352
3353API int
3354nc_session_get_notif_status(const struct nc_session *session)
3355{
3356 if (!session || (session->side != NC_SERVER)) {
3357 ERRARG("session");
3358 return 0;
3359 }
3360
3361 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003362}
Michal Vasko8f430592019-02-26 08:32:54 +01003363
3364API int
3365nc_session_is_callhome(const struct nc_session *session)
3366{
3367 if (!session || (session->side != NC_SERVER)) {
3368 ERRARG("session");
3369 return 0;
3370 }
3371
3372 if (session->flags & NC_SESSION_CALLHOME) {
3373 return 1;
3374 }
3375
3376 return 0;
3377}