blob: 9de4052cbaa52978c0fc7503f247fc707f515089 [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 Vaskobe86fe32016-04-07 10:43:03 +0200884
885 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200886 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200887 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100888 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200889
890 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
891 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +0530892 /**
893 * This may happen when another thread releases the lock and broadcasts the condition
894 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
895 * but when actually this thread was ready for condition.
896 */
preetbhansali629dfc42018-12-17 16:04:40 +0530897 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +0530898 break;
899 }
Michal Vasko66032bc2019-01-22 15:03:12 +0100900
Michal Vasko26043172016-07-26 14:08:59 +0200901 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200902 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200903 nc_ps_queue_remove_id(ps, *id);
904 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200905 return -1;
906 }
907 }
908
Michal Vaskobe86fe32016-04-07 10:43:03 +0200909 /* UNLOCK */
910 pthread_mutex_unlock(&ps->lock);
911
912 return 0;
913}
914
Michal Vaskof04a52a2016-04-07 10:52:10 +0200915int
Michal Vasko26043172016-07-26 14:08:59 +0200916nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200917{
918 int ret;
919 struct timespec ts;
920
Michal Vasko77a6abe2017-10-05 10:02:20 +0200921 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100922 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200923
924 /* LOCK */
925 ret = pthread_mutex_timedlock(&ps->lock, &ts);
926 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200927 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200928 ret = -1;
929 }
930
Michal Vaskob30b99c2016-07-26 11:35:43 +0200931 /* we must be the first, it was our turn after all, right? */
932 if (ps->queue[ps->queue_begin] != id) {
933 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200934 /* UNLOCK */
935 if (!ret) {
936 pthread_mutex_unlock(&ps->lock);
937 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200938 return -1;
939 }
940
Michal Vaskobe86fe32016-04-07 10:43:03 +0200941 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200942 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200943
944 /* broadcast to all other threads that the queue moved */
945 pthread_cond_broadcast(&ps->cond);
946
Michal Vaskobe86fe32016-04-07 10:43:03 +0200947 /* UNLOCK */
948 if (!ret) {
949 pthread_mutex_unlock(&ps->lock);
950 }
951
952 return ret;
953}
954
Michal Vasko428087d2016-01-14 16:04:28 +0100955API struct nc_pollsession *
956nc_ps_new(void)
957{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100958 struct nc_pollsession *ps;
959
960 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100961 if (!ps) {
962 ERRMEM;
963 return NULL;
964 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200965 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100966 pthread_mutex_init(&ps->lock, NULL);
967
968 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100969}
970
971API void
972nc_ps_free(struct nc_pollsession *ps)
973{
fanchanghu3d4e7212017-08-09 09:42:30 +0800974 uint16_t i;
975
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100976 if (!ps) {
977 return;
978 }
979
Michal Vaskobe86fe32016-04-07 10:43:03 +0200980 if (ps->queue_len) {
981 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
982 }
983
fanchanghu3d4e7212017-08-09 09:42:30 +0800984 for (i = 0; i < ps->session_count; i++) {
985 free(ps->sessions[i]);
986 }
987
Michal Vasko428087d2016-01-14 16:04:28 +0100988 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100989 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200990 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100991
Michal Vasko428087d2016-01-14 16:04:28 +0100992 free(ps);
993}
994
995API int
996nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
997{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200998 uint8_t q_id;
999
Michal Vasko45e53ae2016-04-07 11:46:03 +02001000 if (!ps) {
1001 ERRARG("ps");
1002 return -1;
1003 } else if (!session) {
1004 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001005 return -1;
1006 }
1007
Michal Vasko48a63ed2016-03-01 09:48:21 +01001008 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001009 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001010 return -1;
1011 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001012
Michal Vasko428087d2016-01-14 16:04:28 +01001013 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001014 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001015 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001016 ERRMEM;
1017 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001018 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001019 return -1;
1020 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001021 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1022 if (!ps->sessions[ps->session_count - 1]) {
1023 ERRMEM;
1024 --ps->session_count;
1025 /* UNLOCK */
1026 nc_ps_unlock(ps, q_id, __func__);
1027 return -1;
1028 }
1029 ps->sessions[ps->session_count - 1]->session = session;
1030 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001031
Michal Vasko48a63ed2016-03-01 09:48:21 +01001032 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001033 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001034}
1035
Michal Vasko48a63ed2016-03-01 09:48:21 +01001036static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001037_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001038{
1039 uint16_t i;
1040
Radek Krejcid5f978f2016-03-03 13:14:45 +01001041 if (index >= 0) {
1042 i = (uint16_t)index;
1043 goto remove;
1044 }
Michal Vasko428087d2016-01-14 16:04:28 +01001045 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001046 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001047remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001048 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001049 if (i <= ps->session_count) {
1050 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001051 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001052 }
1053 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001054 free(ps->sessions);
1055 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001056 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001057 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001058 return 0;
1059 }
1060 }
1061
Michal Vaskof0537d82016-01-29 14:42:38 +01001062 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001063}
1064
Michal Vasko48a63ed2016-03-01 09:48:21 +01001065API int
1066nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1067{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001068 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001069 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001070
Michal Vasko45e53ae2016-04-07 11:46:03 +02001071 if (!ps) {
1072 ERRARG("ps");
1073 return -1;
1074 } else if (!session) {
1075 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001076 return -1;
1077 }
1078
1079 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001080 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001081 return -1;
1082 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001083
Radek Krejcid5f978f2016-03-03 13:14:45 +01001084 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001085
1086 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001087 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001088
Michal Vaskobe86fe32016-04-07 10:43:03 +02001089 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001090}
1091
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001092API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001093nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001094{
1095 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001096 struct nc_session *ret = NULL;
1097
1098 if (!ps) {
1099 ERRARG("ps");
1100 return NULL;
1101 }
1102
1103 /* LOCK */
1104 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1105 return NULL;
1106 }
1107
Michal Vasko4871c9d2017-10-09 14:48:39 +02001108 if (idx < ps->session_count) {
1109 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001110 }
1111
1112 /* UNLOCK */
1113 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1114
1115 return ret;
1116}
1117
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001118API uint16_t
1119nc_ps_session_count(struct nc_pollsession *ps)
1120{
Michal Vasko47003942019-03-14 12:25:23 +01001121 uint8_t q_id;
1122 uint16_t session_count;
1123
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001124 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001125 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001126 return 0;
1127 }
1128
Michal Vasko47003942019-03-14 12:25:23 +01001129 /* LOCK (just for memory barrier so that we read the current value) */
1130 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1131 return 0;
1132 }
1133
1134 session_count = ps->session_count;
1135
1136 /* UNLOCK */
1137 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1138
1139 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001140}
1141
Michal Vasko131120a2018-05-29 15:44:02 +02001142/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001143 * returns: NC_PSPOLL_ERROR,
1144 * NC_PSPOLL_BAD_RPC,
1145 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1146 * NC_PSPOLL_RPC
1147 */
1148static int
Michal Vasko131120a2018-05-29 15:44:02 +02001149nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001150{
1151 struct lyxml_elem *xml = NULL;
1152 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001153 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001154 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001155
Michal Vasko45e53ae2016-04-07 11:46:03 +02001156 if (!session) {
1157 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001158 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001159 } else if (!rpc) {
1160 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001161 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001162 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001163 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001164 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001165 }
1166
Michal Vasko131120a2018-05-29 15:44:02 +02001167 msgtype = nc_read_msg_io(session, io_timeout, &xml, 0);
Michal Vasko428087d2016-01-14 16:04:28 +01001168
1169 switch (msgtype) {
1170 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001171 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001172 if (!*rpc) {
1173 ERRMEM;
1174 goto error;
1175 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001176
Radek Krejcif93c7d42016-04-06 13:41:15 +02001177 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001178 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1179 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001180 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001181 /* parsing RPC failed */
Michal Vaskoc9970242018-02-14 16:03:35 +01001182 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
Michal Vasko131120a2018-05-29 15:44:02 +02001183 ret = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001184 nc_server_reply_free(reply);
1185 if (ret == -1) {
1186 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001187 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001188 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1189 } else {
1190 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001191 }
Michal Vasko428087d2016-01-14 16:04:28 +01001192 (*rpc)->root = xml;
1193 break;
1194 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001195 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001196 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001197 goto error;
1198 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001199 ERR("Session %u: received <rpc-reply> from a NETCONF client.", 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_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001203 ERR("Session %u: received <notification> 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 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001207 /* NC_MSG_ERROR,
Michal Vasko131120a2018-05-29 15:44:02 +02001208 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg_io()
Michal Vasko428087d2016-01-14 16:04:28 +01001209 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001210 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001211 break;
1212 }
1213
Michal Vasko71090fc2016-05-24 16:37:28 +02001214 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001215
1216error:
1217 /* cleanup */
1218 lyxml_free(server_opts.ctx, xml);
1219
Michal Vasko71090fc2016-05-24 16:37:28 +02001220 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001221}
1222
fanchanghu966f2de2016-07-21 02:28:57 -04001223API void
1224nc_set_global_rpc_clb(nc_rpc_clb clb)
1225{
1226 global_rpc_clb = clb;
1227}
1228
Radek Krejci93e80222016-10-03 13:34:25 +02001229API NC_MSG_TYPE
1230nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1231{
Michal Vasko131120a2018-05-29 15:44:02 +02001232 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001233
1234 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001235 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001236 ERRARG("session");
1237 return NC_MSG_ERROR;
1238 } else if (!notif || !notif->tree || !notif->eventtime) {
1239 ERRARG("notif");
1240 return NC_MSG_ERROR;
1241 }
1242
Michal Vasko131120a2018-05-29 15:44:02 +02001243 /* we do not need RPC lock for this, IO lock will be acquired properly */
1244 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
1245 if (ret == NC_MSG_ERROR) {
Radek Krejci93e80222016-10-03 13:34:25 +02001246 ERR("Session %u: failed to write notification.", session->id);
Radek Krejci93e80222016-10-03 13:34:25 +02001247 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001248
Michal Vasko131120a2018-05-29 15:44:02 +02001249 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001250}
1251
Michal Vasko131120a2018-05-29 15:44:02 +02001252/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001253 * returns: NC_PSPOLL_ERROR,
1254 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1255 * NC_PSPOLL_REPLY_ERROR,
1256 * 0
1257 */
1258static int
Michal Vasko131120a2018-05-29 15:44:02 +02001259nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001260{
1261 nc_rpc_clb clb;
1262 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001263 struct lys_node *rpc_act = NULL;
1264 struct lyd_node *next, *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001265 int ret = 0;
1266 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001267
Michal Vasko4a827e52016-03-03 10:59:00 +01001268 if (!rpc) {
1269 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001270 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001271 }
1272
Michal Vasko90e8e692016-07-13 12:27:57 +02001273 if (rpc->tree->schema->nodetype == LYS_RPC) {
1274 /* RPC */
1275 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001276 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001277 /* action */
1278 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1279 if (elem->schema->nodetype == LYS_ACTION) {
1280 rpc_act = elem->schema;
1281 break;
1282 }
1283 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001284 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001285 if (!rpc_act) {
1286 ERRINT;
1287 return NC_PSPOLL_ERROR;
1288 }
1289 }
1290
1291 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001292 if (!global_rpc_clb) {
1293 /* no callback, reply with a not-implemented error */
1294 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
1295 } else {
1296 reply = global_rpc_clb(rpc->tree, session);
1297 }
Michal Vasko428087d2016-01-14 16:04:28 +01001298 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001299 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001300 reply = clb(rpc->tree, session);
1301 }
1302
1303 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001304 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001305 }
Michal Vasko131120a2018-05-29 15:44:02 +02001306 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->root, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001307 if (reply->type == NC_RPL_ERROR) {
1308 ret |= NC_PSPOLL_REPLY_ERROR;
1309 }
1310 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001311
Michal Vasko131120a2018-05-29 15:44:02 +02001312 if (r != NC_MSG_REPLY) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001313 ERR("Session %u: failed to write reply.", session->id);
1314 ret |= NC_PSPOLL_ERROR;
1315 }
Michal Vasko428087d2016-01-14 16:04:28 +01001316
1317 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1318 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1319 session->status = NC_STATUS_INVALID;
1320 }
1321
Michal Vasko71090fc2016-05-24 16:37:28 +02001322 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001323}
1324
Michal Vasko131120a2018-05-29 15:44:02 +02001325/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001326 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1327 * NC_PSPOLL_ERROR, (msg filled)
1328 * NC_PSPOLL_TIMEOUT,
1329 * NC_PSPOLL_RPC (some application data available),
1330 * NC_PSPOLL_SSH_CHANNEL,
1331 * NC_PSPOLL_SSH_MSG
1332 */
1333static int
Michal Vasko131120a2018-05-29 15:44:02 +02001334nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001335{
Michal Vasko9a327362017-01-11 11:31:46 +01001336 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001337 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001338#ifdef NC_ENABLED_SSH
1339 struct nc_session *new;
1340#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001341
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001342 /* check timeout first */
1343 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001344 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001345 sprintf(msg, "session idle timeout elapsed");
1346 session->status = NC_STATUS_INVALID;
1347 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1348 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1349 }
1350
Michal Vasko131120a2018-05-29 15:44:02 +02001351 r = nc_session_io_lock(session, io_timeout, __func__);
1352 if (r < 0) {
1353 sprintf(msg, "session IO lock failed to be acquired");
1354 return NC_PSPOLL_ERROR;
1355 } else if (!r) {
1356 return NC_PSPOLL_TIMEOUT;
1357 }
1358
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001359 switch (session->ti_type) {
1360#ifdef NC_ENABLED_SSH
1361 case NC_TI_LIBSSH:
1362 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001363 if (r == SSH_EOF) {
1364 sprintf(msg, "SSH channel unexpected EOF");
1365 session->status = NC_STATUS_INVALID;
1366 session->term_reason = NC_SESSION_TERM_DROPPED;
1367 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1368 } else if (r == SSH_ERROR) {
1369 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001370 session->status = NC_STATUS_INVALID;
1371 session->term_reason = NC_SESSION_TERM_OTHER;
1372 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001373 } else if (!r) {
1374 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1375 /* new SSH message */
1376 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1377 if (session->ti.libssh.next) {
1378 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1379 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1380 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1381 /* new NETCONF SSH channel */
1382 ret = NC_PSPOLL_SSH_CHANNEL;
1383 break;
1384 }
1385 }
1386 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001387 break;
1388 }
1389 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001390
Michal Vasko8dcaa882017-10-19 14:28:42 +02001391 /* just some SSH message */
1392 ret = NC_PSPOLL_SSH_MSG;
1393 } else {
1394 ret = NC_PSPOLL_TIMEOUT;
1395 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001396 } else {
1397 /* we have some application data */
1398 ret = NC_PSPOLL_RPC;
1399 }
1400 break;
1401#endif
1402#ifdef NC_ENABLED_TLS
1403 case NC_TI_OPENSSL:
1404 r = SSL_pending(session->ti.tls);
1405 if (!r) {
1406 /* no data pending in the SSL buffer, poll fd */
1407 pfd.fd = SSL_get_rfd(session->ti.tls);
1408 if (pfd.fd < 0) {
1409 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1410 ret = NC_PSPOLL_ERROR;
1411 break;
1412 }
1413 pfd.events = POLLIN;
1414 pfd.revents = 0;
1415 r = poll(&pfd, 1, 0);
1416
1417 if ((r < 0) && (errno != EINTR)) {
1418 sprintf(msg, "poll failed (%s)", strerror(errno));
1419 session->status = NC_STATUS_INVALID;
1420 ret = NC_PSPOLL_ERROR;
1421 } else if (r > 0) {
1422 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1423 sprintf(msg, "communication socket unexpectedly closed");
1424 session->status = NC_STATUS_INVALID;
1425 session->term_reason = NC_SESSION_TERM_DROPPED;
1426 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1427 } else if (pfd.revents & POLLERR) {
1428 sprintf(msg, "communication socket error");
1429 session->status = NC_STATUS_INVALID;
1430 session->term_reason = NC_SESSION_TERM_OTHER;
1431 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1432 } else {
1433 ret = NC_PSPOLL_RPC;
1434 }
1435 } else {
1436 ret = NC_PSPOLL_TIMEOUT;
1437 }
1438 } else {
1439 ret = NC_PSPOLL_RPC;
1440 }
1441 break;
1442#endif
1443 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001444 case NC_TI_UNIX:
1445 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001446 pfd.events = POLLIN;
1447 pfd.revents = 0;
1448 r = poll(&pfd, 1, 0);
1449
1450 if ((r < 0) && (errno != EINTR)) {
1451 sprintf(msg, "poll failed (%s)", strerror(errno));
1452 session->status = NC_STATUS_INVALID;
1453 ret = NC_PSPOLL_ERROR;
1454 } else if (r > 0) {
1455 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1456 sprintf(msg, "communication socket unexpectedly closed");
1457 session->status = NC_STATUS_INVALID;
1458 session->term_reason = NC_SESSION_TERM_DROPPED;
1459 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1460 } else if (pfd.revents & POLLERR) {
1461 sprintf(msg, "communication socket error");
1462 session->status = NC_STATUS_INVALID;
1463 session->term_reason = NC_SESSION_TERM_OTHER;
1464 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1465 } else {
1466 ret = NC_PSPOLL_RPC;
1467 }
1468 } else {
1469 ret = NC_PSPOLL_TIMEOUT;
1470 }
1471 break;
1472 case NC_TI_NONE:
1473 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1474 ret = NC_PSPOLL_ERROR;
1475 break;
1476 }
1477
Michal Vasko131120a2018-05-29 15:44:02 +02001478 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001479 return ret;
1480}
1481
1482API int
1483nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1484{
1485 int ret, r;
1486 uint8_t q_id;
1487 uint16_t i, j;
1488 char msg[256];
1489 struct timespec ts_timeout, ts_cur;
1490 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001491 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001492 struct nc_server_rpc *rpc = NULL;
1493
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001494 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001495 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001496 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001497 }
1498
Michal Vaskoade892d2017-02-22 13:40:35 +01001499 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001500 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001501 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001502 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001503
Michal Vaskoade892d2017-02-22 13:40:35 +01001504 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001505 nc_ps_unlock(ps, q_id, __func__);
1506 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001507 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001508
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001509 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001510 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001511 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001512 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001513 nc_addtimespec(&ts_timeout, timeout);
1514 }
1515
1516 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001517 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001518 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001519 if (ps->last_event_session == ps->session_count - 1) {
1520 i = j = 0;
1521 } else {
1522 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001523 }
Michal Vasko9a327362017-01-11 11:31:46 +01001524 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001525 cur_ps_session = ps->sessions[i];
1526 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001527
Michal Vasko131120a2018-05-29 15:44:02 +02001528 /* SESSION RPC LOCK */
1529 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001530 if (r == -1) {
1531 ret = NC_PSPOLL_ERROR;
1532 } else if (r == 1) {
1533 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001534 switch (cur_ps_session->state) {
1535 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001536 if (cur_session->status == NC_STATUS_RUNNING) {
1537 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001538 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001539
Michal Vasko131120a2018-05-29 15:44:02 +02001540 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001541 switch (ret) {
1542 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1543 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001544 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001545 break;
1546 case NC_PSPOLL_ERROR:
1547 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001548 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001549 break;
1550 case NC_PSPOLL_TIMEOUT:
1551#ifdef NC_ENABLED_SSH
1552 case NC_PSPOLL_SSH_CHANNEL:
1553 case NC_PSPOLL_SSH_MSG:
1554#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001555 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001556 break;
1557 case NC_PSPOLL_RPC:
1558 /* let's keep the state busy, we are not done with this session */
1559 break;
1560 }
1561 } else {
1562 /* session is not fine, let the caller know */
1563 ret = NC_PSPOLL_SESSION_TERM;
1564 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1565 ret |= NC_PSPOLL_SESSION_ERROR;
1566 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001567 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001568 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001569 break;
1570 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001571 /* it definitely should not be busy because we have the lock */
1572 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001573 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001574 break;
1575 case NC_PS_STATE_INVALID:
1576 /* we got it locked, but it will be freed, let it be */
1577 ret = NC_PSPOLL_TIMEOUT;
1578 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001579 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001580
Michal Vasko131120a2018-05-29 15:44:02 +02001581 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001582 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001583 /* SESSION RPC UNLOCK */
1584 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001585 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001586 } else {
1587 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001588 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001589 }
Michal Vasko428087d2016-01-14 16:04:28 +01001590
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001591 /* something happened */
1592 if (ret != NC_PSPOLL_TIMEOUT) {
1593 break;
1594 }
1595
Michal Vasko9a327362017-01-11 11:31:46 +01001596 if (i == ps->session_count - 1) {
1597 i = 0;
1598 } else {
1599 ++i;
1600 }
1601 } while (i != j);
1602
Michal Vaskoade892d2017-02-22 13:40:35 +01001603 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001604 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001605 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001606 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001607 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001608
1609 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1610 /* final timeout */
1611 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001612 }
Michal Vasko428087d2016-01-14 16:04:28 +01001613 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001614 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001615
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001616 /* do we want to return the session? */
1617 switch (ret) {
1618 case NC_PSPOLL_RPC:
1619 case NC_PSPOLL_SESSION_TERM:
1620 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1621#ifdef NC_ENABLED_SSH
1622 case NC_PSPOLL_SSH_CHANNEL:
1623 case NC_PSPOLL_SSH_MSG:
1624#endif
1625 if (session) {
1626 *session = cur_session;
1627 }
1628 ps->last_event_session = i;
1629 break;
1630 default:
1631 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001632 }
Michal Vasko428087d2016-01-14 16:04:28 +01001633
Michal Vaskoade892d2017-02-22 13:40:35 +01001634 /* PS UNLOCK */
1635 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001636
Michal Vasko131120a2018-05-29 15:44:02 +02001637 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001638 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001639 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001640 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1641 if (cur_session->status != NC_STATUS_RUNNING) {
1642 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001643 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001644 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001645 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001646 }
1647 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001648 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001649
Michal Vasko7f1ee932018-10-11 09:41:42 +02001650 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001651 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001652 if (cur_session->status != NC_STATUS_RUNNING) {
1653 ret |= NC_PSPOLL_SESSION_TERM;
1654 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1655 ret |= NC_PSPOLL_SESSION_ERROR;
1656 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001657 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001658 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001659 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001660 }
Michal Vasko428087d2016-01-14 16:04:28 +01001661 }
Michal Vasko7f1ee932018-10-11 09:41:42 +02001662 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001663
Michal Vasko131120a2018-05-29 15:44:02 +02001664 /* SESSION RPC UNLOCK */
1665 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001666 }
1667
Michal Vasko48a63ed2016-03-01 09:48:21 +01001668 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001669}
1670
Michal Vaskod09eae62016-02-01 10:32:52 +01001671API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001672nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001673{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001674 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001675 uint16_t i;
1676 struct nc_session *session;
1677
Michal Vasko9a25e932016-02-01 10:36:42 +01001678 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001679 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001680 return;
1681 }
1682
Michal Vasko48a63ed2016-03-01 09:48:21 +01001683 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001684 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001685 return;
1686 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001687
Michal Vasko48a63ed2016-03-01 09:48:21 +01001688 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001689 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001690 nc_session_free(ps->sessions[i]->session, data_free);
1691 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001692 }
1693 free(ps->sessions);
1694 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001695 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001696 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001697 } else {
1698 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001699 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1700 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001701 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001702 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001703 continue;
1704 }
1705
1706 ++i;
1707 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001708 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001709
1710 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001711 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001712}
1713
Radek Krejci53691be2016-02-22 13:58:37 +01001714#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001715
Michal Vasko5f352c52019-07-10 16:12:06 +02001716static int
1717nc_accept_unix(struct nc_session *session, int sock)
1718{
1719 const struct passwd *pw;
1720 struct ucred ucred;
1721 char *username;
1722 socklen_t len;
1723
1724 session->ti_type = NC_TI_UNIX;
1725
1726 len = sizeof(ucred);
1727 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
1728 ERR("Failed to get credentials from unix socket (%s).",
1729 strerror(errno));
1730 close(sock);
1731 return -1;
1732 }
1733
1734 pw = getpwuid(ucred.uid);
1735 if (pw == NULL) {
1736 ERR("Failed to find username for uid=%u (%s).\n", ucred.uid,
1737 strerror(errno));
1738 close(sock);
1739 return -1;
1740 }
1741
1742 username = strdup(pw->pw_name);
1743 if (username == NULL) {
1744 ERRMEM;
1745 close(sock);
1746 return -1;
1747 }
1748 session->username = lydict_insert_zc(server_opts.ctx, username);
1749
1750 session->ti.unixsock.sock = sock;
1751
1752 return 1;
1753}
1754
Michal Vaskoe2713da2016-08-22 16:06:40 +02001755API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001756nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001757{
Michal Vasko3031aae2016-01-27 16:07:18 +01001758 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001759 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001760
Michal Vasko45e53ae2016-04-07 11:46:03 +02001761 if (!name) {
1762 ERRARG("name");
1763 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001764 }
1765
Michal Vaskoade892d2017-02-22 13:40:35 +01001766 /* BIND LOCK */
1767 pthread_mutex_lock(&server_opts.bind_lock);
1768
1769 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001770 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001771
1772 /* check name uniqueness */
1773 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001774 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001775 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001776 ret = -1;
1777 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001778 }
1779 }
1780
Michal Vasko3031aae2016-01-27 16:07:18 +01001781 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001782 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001783 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001784 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001785 ret = -1;
1786 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001787 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001788 memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001789 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001790 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001791 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1792 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1793 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001794
Michal Vaskoe2713da2016-08-22 16:06:40 +02001795 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001796 if (!server_opts.binds) {
1797 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001798 ret = -1;
1799 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001800 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001801
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001802 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001803 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1804
1805 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001806#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001807 case NC_TI_LIBSSH:
1808 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1809 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1810 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001811 ret = -1;
1812 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001813 }
1814 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1815 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1816 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02001817 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001818 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001819#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001820#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001821 case NC_TI_OPENSSL:
1822 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1823 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1824 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001825 ret = -1;
1826 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001827 }
1828 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001829#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001830 case NC_TI_UNIX:
1831 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1832 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1833 ERRMEM;
1834 ret = -1;
1835 goto cleanup;
1836 }
1837 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1838 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1839 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1840 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001841 default:
1842 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001843 ret = -1;
1844 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001845 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001846
Michal Vaskoade892d2017-02-22 13:40:35 +01001847cleanup:
1848 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001849 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001850
Michal Vaskoade892d2017-02-22 13:40:35 +01001851 /* BIND UNLOCK */
1852 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001853
Michal Vaskoade892d2017-02-22 13:40:35 +01001854 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001855}
1856
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001857API int
Michal Vasko59050372016-11-22 14:33:55 +01001858nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001859{
1860 uint32_t i;
1861 int ret = -1;
1862
Michal Vaskoade892d2017-02-22 13:40:35 +01001863 /* BIND LOCK */
1864 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001865
Michal Vaskoade892d2017-02-22 13:40:35 +01001866 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001867 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001868
Michal Vasko59050372016-11-22 14:33:55 +01001869 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001870 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001871 for (i = 0; i < server_opts.endpt_count; ++i) {
1872 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001873 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001874#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001875 case NC_TI_LIBSSH:
1876 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1877 free(server_opts.endpts[i].opts.ssh);
1878 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001879#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001880#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001881 case NC_TI_OPENSSL:
1882 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1883 free(server_opts.endpts[i].opts.tls);
1884 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001885#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001886 case NC_TI_UNIX:
1887 free(server_opts.endpts[i].opts.unixsock);
1888 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001889 default:
1890 ERRINT;
1891 /* won't get here ...*/
1892 break;
1893 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001894 ret = 0;
1895 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001896 free(server_opts.endpts);
1897 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001898
1899 /* remove all binds */
1900 for (i = 0; i < server_opts.endpt_count; ++i) {
1901 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1902 if (server_opts.binds[i].sock > -1) {
1903 close(server_opts.binds[i].sock);
1904 }
1905 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001906 free(server_opts.binds);
1907 server_opts.binds = NULL;
1908
Michal Vasko3031aae2016-01-27 16:07:18 +01001909 server_opts.endpt_count = 0;
1910
Michal Vasko1a38c862016-01-15 15:50:07 +01001911 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001912 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001913 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001914 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001915 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001916 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001917 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001918#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001919 case NC_TI_LIBSSH:
1920 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1921 free(server_opts.endpts[i].opts.ssh);
1922 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001923#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001924#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001925 case NC_TI_OPENSSL:
1926 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1927 free(server_opts.endpts[i].opts.tls);
1928 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001929#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001930 case NC_TI_UNIX:
1931 free(server_opts.endpts[i].opts.unixsock);
1932 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001933 default:
1934 ERRINT;
1935 break;
1936 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001937
Michal Vaskoe2713da2016-08-22 16:06:40 +02001938 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001939 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1940 if (server_opts.binds[i].sock > -1) {
1941 close(server_opts.binds[i].sock);
1942 }
1943
1944 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001945 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001946 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001947 free(server_opts.binds);
1948 server_opts.binds = NULL;
1949 free(server_opts.endpts);
1950 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001951 } else if (i < server_opts.endpt_count) {
1952 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1953 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001954 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001955
1956 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001957 if (name) {
1958 break;
1959 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001960 }
1961 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001962 }
1963
Michal Vaskoade892d2017-02-22 13:40:35 +01001964 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001965 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001966
Michal Vaskoade892d2017-02-22 13:40:35 +01001967 /* BIND UNLOCK */
1968 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001969
1970 return ret;
1971}
1972
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001973API int
1974nc_server_endpt_count(void)
1975{
1976 return server_opts.endpt_count;
1977}
1978
1979int
1980nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
1981{
1982 struct nc_endpt *endpt;
1983 struct nc_bind *bind = NULL;
1984 uint16_t i;
1985 int sock = -1, set_addr, ret = 0;
1986
1987 if (!endpt_name) {
1988 ERRARG("endpt_name");
1989 return -1;
1990 } else if ((!address && !port) || (address && port)) {
1991 ERRARG("address and port");
1992 return -1;
1993 }
1994
1995 if (address) {
1996 set_addr = 1;
1997 } else {
1998 set_addr = 0;
1999 }
2000
2001 /* BIND LOCK */
2002 pthread_mutex_lock(&server_opts.bind_lock);
2003
2004 /* ENDPT LOCK */
2005 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2006 if (!endpt) {
2007 /* BIND UNLOCK */
2008 pthread_mutex_unlock(&server_opts.bind_lock);
2009 return -1;
2010 }
2011
2012 bind = &server_opts.binds[i];
2013
2014 if (set_addr) {
2015 port = bind->port;
2016 } else {
2017 address = bind->address;
2018 }
2019
2020 if (!set_addr && endpt->ti == NC_TI_UNIX) {
2021 ret = -1;
2022 goto cleanup;
2023 }
2024
2025 /* we have all the information we need to create a listening socket */
2026 if (address && (port || endpt->ti == NC_TI_UNIX)) {
2027 /* create new socket, close the old one */
2028 if (endpt->ti == NC_TI_UNIX)
2029 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
2030 else
2031 sock = nc_sock_listen_inet(address, port, &endpt->ka);
2032 if (sock == -1) {
2033 ret = -1;
2034 goto cleanup;
2035 }
2036
2037 if (bind->sock > -1) {
2038 close(bind->sock);
2039 }
2040 bind->sock = sock;
2041 } /* else we are just setting address or port */
2042
2043 if (set_addr) {
2044 lydict_remove(server_opts.ctx, bind->address);
2045 bind->address = lydict_insert(server_opts.ctx, address, 0);
2046 } else {
2047 bind->port = port;
2048 }
2049
2050 if (sock > -1) {
2051#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
2052 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
2053#elif defined(NC_ENABLED_SSH)
2054 VRB("Listening on %s:%u for SSH connections.", address, port);
2055#else
2056 VRB("Listening on %s:%u for TLS connections.", address, port);
2057#endif
2058 }
2059
2060cleanup:
2061 /* ENDPT UNLOCK */
2062 pthread_rwlock_unlock(&server_opts.endpt_lock);
2063
2064 /* BIND UNLOCK */
2065 pthread_mutex_unlock(&server_opts.bind_lock);
2066
2067 return ret;
2068}
2069
2070API int
2071nc_server_endpt_set_address(const char *endpt_name, const char *address)
2072{
2073 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2074}
2075
2076API int
2077nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2078{
2079 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2080}
2081
2082API int
2083nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2084{
2085 struct nc_endpt *endpt;
2086 uint16_t i;
2087 int ret = 0;
2088
2089 if (!endpt_name) {
2090 ERRARG("endpt_name");
2091 return -1;
2092 } else if (mode == 0) {
2093 ERRARG("mode");
2094 return -1;
2095 }
2096
2097 /* ENDPT LOCK */
2098 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2099 if (!endpt)
2100 return -1;
2101
2102 if (endpt->ti != NC_TI_UNIX) {
2103 ret = -1;
2104 goto cleanup;
2105 }
2106
2107 endpt->opts.unixsock->mode = mode;
2108 endpt->opts.unixsock->uid = uid;
2109 endpt->opts.unixsock->gid = gid;
2110
2111cleanup:
2112 /* ENDPT UNLOCK */
2113 pthread_rwlock_unlock(&server_opts.endpt_lock);
2114
2115 return ret;
2116}
2117
2118API int
2119nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2120{
2121 struct nc_endpt *endpt;
2122 int ret = 0;
2123
2124 if (!endpt_name) {
2125 ERRARG("endpt_name");
2126 return -1;
2127 }
2128
2129 /* ENDPT LOCK */
2130 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2131 if (!endpt) {
2132 return -1;
2133 }
2134
2135 endpt->ka.enabled = (enable ? 1 : 0);
2136
2137 /* ENDPT UNLOCK */
2138 pthread_rwlock_unlock(&server_opts.endpt_lock);
2139
2140 return ret;
2141}
2142
2143API int
2144nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2145{
2146 struct nc_endpt *endpt;
2147 int ret = 0;
2148
2149 if (!endpt_name) {
2150 ERRARG("endpt_name");
2151 return -1;
2152 }
2153
2154 /* ENDPT LOCK */
2155 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2156 if (!endpt) {
2157 return -1;
2158 }
2159
2160 if (idle_time > -1) {
2161 endpt->ka.idle_time = idle_time;
2162 }
2163 if (max_probes > -1) {
2164 endpt->ka.max_probes = max_probes;
2165 }
2166 if (probe_interval > -1) {
2167 endpt->ka.probe_interval = probe_interval;
2168 }
2169
2170 /* ENDPT UNLOCK */
2171 pthread_rwlock_unlock(&server_opts.endpt_lock);
2172
2173 return ret;
2174}
2175
Michal Vasko71090fc2016-05-24 16:37:28 +02002176API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002177nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002178{
Michal Vasko71090fc2016-05-24 16:37:28 +02002179 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002180 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002181 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002182 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002183 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002184
Michal Vasko45e53ae2016-04-07 11:46:03 +02002185 if (!server_opts.ctx) {
2186 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002187 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002188 } else if (!session) {
2189 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002190 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002191 }
2192
Michal Vaskoade892d2017-02-22 13:40:35 +01002193 /* BIND LOCK */
2194 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002195
2196 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002197 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002198 /* BIND UNLOCK */
2199 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002200 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002201 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002202
Michal Vaskoe2713da2016-08-22 16:06:40 +02002203 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002204 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002205 /* BIND UNLOCK */
2206 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002207 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002208 if (!ret) {
2209 return NC_MSG_WOULDBLOCK;
2210 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002211 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002212 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002213
2214 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2215 /* ENDPT READ LOCK */
2216 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2217
2218 /* BIND UNLOCK */
2219 pthread_mutex_unlock(&server_opts.bind_lock);
2220
Michal Vaskob48aa812016-01-18 14:13:09 +01002221 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002222
Michal Vasko131120a2018-05-29 15:44:02 +02002223 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002224 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002225 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002226 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002227 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002228 msgtype = NC_MSG_ERROR;
2229 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002230 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002231 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002232 (*session)->ctx = server_opts.ctx;
2233 (*session)->flags = NC_SESSION_SHAREDCTX;
2234 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
2235 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002236
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002237 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002238#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002239 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2240 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002241 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002242 if (ret < 0) {
2243 msgtype = NC_MSG_ERROR;
2244 goto cleanup;
2245 } else if (!ret) {
2246 msgtype = NC_MSG_WOULDBLOCK;
2247 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002248 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002249 } else
2250#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002251#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002252 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2253 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002254 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002255 if (ret < 0) {
2256 msgtype = NC_MSG_ERROR;
2257 goto cleanup;
2258 } else if (!ret) {
2259 msgtype = NC_MSG_WOULDBLOCK;
2260 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002261 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002262 } else
2263#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002264 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2265 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2266 ret = nc_accept_unix(*session, sock);
2267 if (ret < 0) {
2268 msgtype = NC_MSG_ERROR;
2269 goto cleanup;
2270 }
2271 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002272 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002273 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002274 msgtype = NC_MSG_ERROR;
2275 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002276 }
2277
Michal Vasko2cc4c682016-03-01 09:16:48 +01002278 (*session)->data = NULL;
2279
Michal Vaskoade892d2017-02-22 13:40:35 +01002280 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002281 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002282
Michal Vaskob48aa812016-01-18 14:13:09 +01002283 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002284 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002285
Michal Vasko9e036d52016-01-08 10:49:26 +01002286 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002287 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002288 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002289 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002290 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002291 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002292 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002293
2294 nc_gettimespec_mono(&ts_cur);
2295 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2296 nc_gettimespec_real(&ts_cur);
2297 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002298 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002299
Michal Vasko71090fc2016-05-24 16:37:28 +02002300 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002301
Michal Vasko71090fc2016-05-24 16:37:28 +02002302cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002303 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002304 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002305
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002306 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002307 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002308 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002309}
2310
Michal Vaskoadf30f02019-06-24 09:34:47 +02002311/* client is expected to be locked */
2312static int
2313_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 +02002314{
2315 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002316 int ret = -1;
2317
2318 if (!endpt_name) {
2319 /* remove all endpoints */
2320 for (i = 0; i < client->ch_endpt_count; ++i) {
2321 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2322 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2323 if (client->ch_endpts[i].sock_pending != -1) {
2324 close(client->ch_endpts[i].sock_pending);
2325 }
2326 switch (client->ch_endpts[i].ti) {
2327#ifdef NC_ENABLED_SSH
2328 case NC_TI_LIBSSH:
2329 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2330 free(client->ch_endpts[i].opts.ssh);
2331 break;
2332#endif
2333#ifdef NC_ENABLED_TLS
2334 case NC_TI_OPENSSL:
2335 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2336 free(client->ch_endpts[i].opts.tls);
2337 break;
2338#endif
2339 default:
2340 ERRINT;
2341 /* won't get here ...*/
2342 break;
2343 }
2344 }
2345 free(client->ch_endpts);
2346 client->ch_endpts = NULL;
2347 client->ch_endpt_count = 0;
2348
2349 ret = 0;
2350 } else {
2351 for (i = 0; i < client->ch_endpt_count; ++i) {
2352 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
2353 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2354 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2355 if (client->ch_endpts[i].sock_pending != -1) {
2356 close(client->ch_endpts[i].sock_pending);
2357 }
2358 switch (client->ch_endpts[i].ti) {
2359#ifdef NC_ENABLED_SSH
2360 case NC_TI_LIBSSH:
2361 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2362 free(client->ch_endpts[i].opts.ssh);
2363 break;
2364#endif
2365#ifdef NC_ENABLED_TLS
2366 case NC_TI_OPENSSL:
2367 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2368 free(client->ch_endpts[i].opts.tls);
2369 break;
2370#endif
2371 default:
2372 ERRINT;
2373 /* won't get here ...*/
2374 break;
2375 }
2376
2377 /* move last endpoint to the empty space */
2378 --client->ch_endpt_count;
2379 if (i < client->ch_endpt_count) {
2380 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2381 } else if (!server_opts.ch_client_count) {
2382 free(server_opts.ch_clients);
2383 server_opts.ch_clients = NULL;
2384 }
2385
2386 ret = 0;
2387 break;
2388 }
2389 }
2390 }
2391
2392 return ret;
2393}
2394
2395API int
2396nc_server_ch_add_client(const char *name)
2397{
2398 uint16_t i;
2399 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002400
2401 if (!name) {
2402 ERRARG("name");
2403 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002404 }
2405
2406 /* WRITE LOCK */
2407 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2408
2409 /* check name uniqueness */
2410 for (i = 0; i < server_opts.ch_client_count; ++i) {
2411 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2412 ERR("Call Home client \"%s\" already exists.", name);
2413 /* WRITE UNLOCK */
2414 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2415 return -1;
2416 }
2417 }
2418
2419 ++server_opts.ch_client_count;
2420 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2421 if (!server_opts.ch_clients) {
2422 ERRMEM;
2423 /* WRITE UNLOCK */
2424 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2425 return -1;
2426 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002427 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002428
Michal Vaskoadf30f02019-06-24 09:34:47 +02002429 client->name = lydict_insert(server_opts.ctx, name, 0);
2430 client->id = ATOMIC_INC(&server_opts.new_client_id);
2431 client->ch_endpts = NULL;
2432 client->ch_endpt_count = 0;
2433 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002434
Michal Vasko2e6defd2016-10-07 15:48:15 +02002435 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002436 client->start_with = NC_CH_FIRST_LISTED;
2437 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002438
Michal Vaskoadf30f02019-06-24 09:34:47 +02002439 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002440
2441 /* WRITE UNLOCK */
2442 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2443
2444 return 0;
2445}
2446
2447API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002448nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002449{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002450 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002451 int ret = -1;
2452
2453 /* WRITE LOCK */
2454 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2455
Michal Vaskoadf30f02019-06-24 09:34:47 +02002456 if (!name) {
2457 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002458 for (i = 0; i < server_opts.ch_client_count; ++i) {
2459 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2460
2461 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002462 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002463
2464 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002465 ret = 0;
2466 }
2467 free(server_opts.ch_clients);
2468 server_opts.ch_clients = NULL;
2469
2470 server_opts.ch_client_count = 0;
2471
2472 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002473 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002474 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002475 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002476 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2477
Michal Vasko2e6defd2016-10-07 15:48:15 +02002478 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002479 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002480
2481 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2482
2483 /* move last client and endpoint(s) to the empty space */
2484 --server_opts.ch_client_count;
2485 if (i < server_opts.ch_client_count) {
2486 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002487 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002488 } else if (!server_opts.ch_client_count) {
2489 free(server_opts.ch_clients);
2490 server_opts.ch_clients = NULL;
2491 }
2492
2493 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002494 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002495 }
2496 }
2497 }
2498
2499 /* WRITE UNLOCK */
2500 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2501
2502 return ret;
2503}
2504
2505API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002506nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002507{
2508 uint16_t i;
2509 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002510 struct nc_ch_endpt *endpt;
2511 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002512
2513 if (!client_name) {
2514 ERRARG("client_name");
2515 return -1;
2516 } else if (!endpt_name) {
2517 ERRARG("endpt_name");
2518 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002519 } else if (!ti) {
2520 ERRARG("ti");
2521 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002522 }
2523
2524 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002525 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002526 if (!client) {
2527 return -1;
2528 }
2529
2530 for (i = 0; i < client->ch_endpt_count; ++i) {
2531 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2532 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002533 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002534 }
2535 }
2536
2537 ++client->ch_endpt_count;
2538 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2539 if (!client->ch_endpts) {
2540 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002541 goto cleanup;
2542 }
2543 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2544
2545 memset(endpt, 0, sizeof *client->ch_endpts);
2546 endpt->name = lydict_insert(server_opts.ctx, endpt_name, 0);
2547 endpt->ti = ti;
2548 endpt->sock_pending = -1;
2549 endpt->ka.idle_time = 1;
2550 endpt->ka.max_probes = 10;
2551 endpt->ka.probe_interval = 5;
2552
2553 switch (ti) {
2554#ifdef NC_ENABLED_SSH
2555 case NC_TI_LIBSSH:
2556 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2557 if (!endpt->opts.ssh) {
2558 ERRMEM;
2559 goto cleanup;
2560 }
2561 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2562 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002563 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002564 break;
2565#endif
2566#ifdef NC_ENABLED_TLS
2567 case NC_TI_OPENSSL:
2568 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2569 if (!endpt->opts.tls) {
2570 ERRMEM;
2571 goto cleanup;
2572 }
2573 break;
2574#endif
2575 default:
2576 ERRINT;
2577 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002578 }
2579
Michal Vaskoadf30f02019-06-24 09:34:47 +02002580 /* success */
2581 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002582
Michal Vaskoadf30f02019-06-24 09:34:47 +02002583cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002584 /* UNLOCK */
2585 nc_server_ch_client_unlock(client);
2586
Michal Vaskoadf30f02019-06-24 09:34:47 +02002587 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002588}
2589
2590API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002591nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002592{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002593 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002594 struct nc_ch_client *client;
2595
2596 if (!client_name) {
2597 ERRARG("client_name");
2598 return -1;
2599 }
2600
2601 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002602 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002603 if (!client) {
2604 return -1;
2605 }
2606
Michal Vaskoadf30f02019-06-24 09:34:47 +02002607 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002608
2609 /* UNLOCK */
2610 nc_server_ch_client_unlock(client);
2611
2612 return ret;
2613}
2614
2615API int
2616nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2617{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002618 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002619 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002620
2621 if (!client_name) {
2622 ERRARG("client_name");
2623 return -1;
2624 } else if (!endpt_name) {
2625 ERRARG("endpt_name");
2626 return -1;
2627 } else if (!address) {
2628 ERRARG("address");
2629 return -1;
2630 }
2631
2632 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002633 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2634 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002635 return -1;
2636 }
2637
Michal Vaskoadf30f02019-06-24 09:34:47 +02002638 lydict_remove(server_opts.ctx, endpt->address);
2639 endpt->address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002640
2641 /* UNLOCK */
2642 nc_server_ch_client_unlock(client);
2643
Michal Vaskoadf30f02019-06-24 09:34:47 +02002644 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002645}
2646
2647API int
2648nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2649{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002650 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002651 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002652
2653 if (!client_name) {
2654 ERRARG("client_name");
2655 return -1;
2656 } else if (!endpt_name) {
2657 ERRARG("endpt_name");
2658 return -1;
2659 } else if (!port) {
2660 ERRARG("port");
2661 return -1;
2662 }
2663
2664 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002665 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2666 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002667 return -1;
2668 }
2669
Michal Vaskoadf30f02019-06-24 09:34:47 +02002670 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002671
2672 /* UNLOCK */
2673 nc_server_ch_client_unlock(client);
2674
Michal Vaskoadf30f02019-06-24 09:34:47 +02002675 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002676}
2677
2678API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002679nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2680{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002681 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002682 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002683
2684 if (!client_name) {
2685 ERRARG("client_name");
2686 return -1;
2687 } else if (!endpt_name) {
2688 ERRARG("endpt_name");
2689 return -1;
2690 }
2691
2692 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002693 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2694 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002695 return -1;
2696 }
2697
Michal Vaskoadf30f02019-06-24 09:34:47 +02002698 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002699
2700 /* UNLOCK */
2701 nc_server_ch_client_unlock(client);
2702
Michal Vaskoadf30f02019-06-24 09:34:47 +02002703 return -1;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002704}
2705
2706API int
2707nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
2708 int probe_interval)
2709{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002710 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002711 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002712
2713 if (!client_name) {
2714 ERRARG("client_name");
2715 return -1;
2716 } else if (!endpt_name) {
2717 ERRARG("endpt_name");
2718 return -1;
2719 }
2720
2721 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002722 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2723 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002724 return -1;
2725 }
2726
Michal Vaskoadf30f02019-06-24 09:34:47 +02002727 if (idle_time > -1) {
2728 endpt->ka.idle_time = idle_time;
2729 }
2730 if (max_probes > -1) {
2731 endpt->ka.max_probes = max_probes;
2732 }
2733 if (probe_interval > -1) {
2734 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002735 }
2736
2737 /* UNLOCK */
2738 nc_server_ch_client_unlock(client);
2739
Michal Vaskoadf30f02019-06-24 09:34:47 +02002740 return -1;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002741}
2742
2743API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002744nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2745{
2746 struct nc_ch_client *client;
2747
2748 if (!client_name) {
2749 ERRARG("client_name");
2750 return -1;
2751 } else if (!conn_type) {
2752 ERRARG("conn_type");
2753 return -1;
2754 }
2755
2756 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002757 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002758 if (!client) {
2759 return -1;
2760 }
2761
2762 if (client->conn_type != conn_type) {
2763 client->conn_type = conn_type;
2764
2765 /* set default options */
2766 switch (conn_type) {
2767 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002768 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002769 break;
2770 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002771 client->conn.period.period = 60;
2772 client->conn.period.anchor_time = 0;
2773 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002774 break;
2775 default:
2776 ERRINT;
2777 break;
2778 }
2779 }
2780
2781 /* UNLOCK */
2782 nc_server_ch_client_unlock(client);
2783
2784 return 0;
2785}
2786
2787API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002788nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
2789{
2790 struct nc_ch_client *client;
2791
2792 if (!client_name) {
2793 ERRARG("client_name");
2794 return -1;
2795 } else if (!period) {
2796 ERRARG("period");
2797 return -1;
2798 }
2799
2800 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002801 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002802 if (!client) {
2803 return -1;
2804 }
2805
2806 if (client->conn_type != NC_CH_PERIOD) {
2807 ERR("Call Home client \"%s\" is not of periodic connection type.");
2808 /* UNLOCK */
2809 nc_server_ch_client_unlock(client);
2810 return -1;
2811 }
2812
2813 client->conn.period.period = period;
2814
2815 /* UNLOCK */
2816 nc_server_ch_client_unlock(client);
2817
2818 return 0;
2819}
2820
2821API int
2822nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002823{
2824 struct nc_ch_client *client;
2825
2826 if (!client_name) {
2827 ERRARG("client_name");
2828 return -1;
2829 }
2830
2831 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002832 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002833 if (!client) {
2834 return -1;
2835 }
2836
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002837 if (client->conn_type != NC_CH_PERIOD) {
2838 ERR("Call Home client \"%s\" is not of periodic connection type.");
Michal Vasko2e6defd2016-10-07 15:48:15 +02002839 /* UNLOCK */
2840 nc_server_ch_client_unlock(client);
2841 return -1;
2842 }
2843
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002844 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002845
2846 /* UNLOCK */
2847 nc_server_ch_client_unlock(client);
2848
2849 return 0;
2850}
2851
2852API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002853nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002854{
2855 struct nc_ch_client *client;
2856
2857 if (!client_name) {
2858 ERRARG("client_name");
2859 return -1;
2860 }
2861
2862 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002863 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002864 if (!client) {
2865 return -1;
2866 }
2867
2868 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002869 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002870 /* UNLOCK */
2871 nc_server_ch_client_unlock(client);
2872 return -1;
2873 }
2874
2875 client->conn.period.idle_timeout = idle_timeout;
2876
2877 /* UNLOCK */
2878 nc_server_ch_client_unlock(client);
2879
2880 return 0;
2881}
2882
2883API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002884nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2885{
2886 struct nc_ch_client *client;
2887
2888 if (!client_name) {
2889 ERRARG("client_name");
2890 return -1;
2891 }
2892
2893 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002894 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002895 if (!client) {
2896 return -1;
2897 }
2898
2899 client->start_with = start_with;
2900
2901 /* UNLOCK */
2902 nc_server_ch_client_unlock(client);
2903
2904 return 0;
2905}
2906
2907API int
2908nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2909{
2910 struct nc_ch_client *client;
2911
2912 if (!client_name) {
2913 ERRARG("client_name");
2914 return -1;
2915 } else if (!max_attempts) {
2916 ERRARG("max_attempts");
2917 return -1;
2918 }
2919
2920 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002921 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002922 if (!client) {
2923 return -1;
2924 }
2925
2926 client->max_attempts = max_attempts;
2927
2928 /* UNLOCK */
2929 nc_server_ch_client_unlock(client);
2930
2931 return 0;
2932}
2933
2934/* client lock is expected to be held */
2935static NC_MSG_TYPE
Michal Vaskoadf30f02019-06-24 09:34:47 +02002936nc_connect_ch_endpt(struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01002937{
Michal Vasko71090fc2016-05-24 16:37:28 +02002938 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002939 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002940 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01002941 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01002942
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002943 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->ka, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002944 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002945 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002946 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00002947 /* no need to store the socket as pending any longer */
2948 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01002949
Michal Vasko131120a2018-05-29 15:44:02 +02002950 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002951 if (!(*session)) {
2952 ERRMEM;
2953 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01002954 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002955 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002956 }
2957 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01002958 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002959 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko66032bc2019-01-22 15:03:12 +01002960 (*session)->host = lydict_insert_zc(server_opts.ctx, ip_host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002961 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002962
Michal Vaskob05053d2016-01-22 16:12:06 +01002963 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002964#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02002965 if (endpt->ti == NC_TI_LIBSSH) {
2966 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002967 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002968 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002969
Michal Vasko71090fc2016-05-24 16:37:28 +02002970 if (ret < 0) {
2971 msgtype = NC_MSG_ERROR;
2972 goto fail;
2973 } else if (!ret) {
2974 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002975 goto fail;
2976 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002977 } else
2978#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002979#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02002980 if (endpt->ti == NC_TI_OPENSSL) {
2981 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002982 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002983 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002984
Michal Vasko71090fc2016-05-24 16:37:28 +02002985 if (ret < 0) {
2986 msgtype = NC_MSG_ERROR;
2987 goto fail;
2988 } else if (!ret) {
2989 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002990 goto fail;
2991 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002992 } else
2993#endif
2994 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002995 ERRINT;
2996 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002997 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002998 goto fail;
2999 }
3000
3001 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01003002 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003003
3004 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003005 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003006 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003007 goto fail;
3008 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003009
3010 nc_gettimespec_mono(&ts_cur);
3011 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3012 nc_gettimespec_real(&ts_cur);
3013 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003014 (*session)->status = NC_STATUS_RUNNING;
3015
Michal Vasko71090fc2016-05-24 16:37:28 +02003016 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003017
3018fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003019 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003020 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003021 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003022}
3023
Michal Vasko2e6defd2016-10-07 15:48:15 +02003024struct nc_ch_client_thread_arg {
3025 char *client_name;
3026 void (*session_clb)(const char *client_name, struct nc_session *new_session);
3027};
3028
3029static struct nc_ch_client *
3030nc_server_ch_client_with_endpt_lock(const char *name)
3031{
3032 struct nc_ch_client *client;
3033
3034 while (1) {
3035 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003036 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003037 if (!client) {
3038 return NULL;
3039 }
3040 if (client->ch_endpt_count) {
3041 return client;
3042 }
3043 /* no endpoints defined yet */
3044
3045 /* UNLOCK */
3046 nc_server_ch_client_unlock(client);
3047
3048 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3049 }
3050
3051 return NULL;
3052}
3053
3054static int
3055nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3056{
Michal Vasko3f05a092018-03-13 10:39:49 +01003057 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003058 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003059 struct timespec ts;
3060 struct nc_ch_client *client;
3061
3062 /* session created, initialize condition */
Michal Vasko27377422018-03-15 08:59:35 +01003063 session->opts.server.ch_lock = calloc(1, sizeof *session->opts.server.ch_lock);
3064 session->opts.server.ch_cond = calloc(1, sizeof *session->opts.server.ch_cond);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003065 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
3066 ERRMEM;
3067 nc_session_free(session, NULL);
3068 return -1;
3069 }
3070 pthread_mutex_init(session->opts.server.ch_lock, NULL);
3071 pthread_cond_init(session->opts.server.ch_cond, NULL);
3072
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003073 session->flags |= NC_SESSION_CALLHOME;
3074
Michal Vasko2e6defd2016-10-07 15:48:15 +02003075 /* CH LOCK */
3076 pthread_mutex_lock(session->opts.server.ch_lock);
3077
3078 /* give the session to the user */
3079 data->session_clb(data->client_name, session);
3080
3081 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003082 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003083 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003084
Michal Vasko3f05a092018-03-13 10:39:49 +01003085 r = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
3086 if (!r) {
3087 /* we were woken up, something probably happened */
3088 if (session->status != NC_STATUS_RUNNING) {
3089 break;
3090 }
3091 } else if (r != ETIMEDOUT) {
3092 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3093 ret = -1;
3094 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003095 }
3096
Michal Vasko2e6defd2016-10-07 15:48:15 +02003097 /* check whether the client was not removed */
3098 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003099 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003100 if (!client) {
3101 /* client was removed, finish thread */
3102 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003103 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003104 ret = 1;
3105 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003106 }
3107
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003108 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003109 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003110 } else {
3111 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003112 }
3113
Michal Vasko9fb42272017-10-05 13:50:05 +02003114 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003115 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 +02003116 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3117 session->status = NC_STATUS_INVALID;
3118 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3119 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003120
3121 /* UNLOCK */
3122 nc_server_ch_client_unlock(client);
3123
3124 } while (session->status == NC_STATUS_RUNNING);
3125
Michal Vasko27377422018-03-15 08:59:35 +01003126 /* CH UNLOCK */
3127 pthread_mutex_unlock(session->opts.server.ch_lock);
3128
Michal Vasko3f05a092018-03-13 10:39:49 +01003129 if (session->status == NC_STATUS_CLOSING) {
3130 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
3131 session->flags &= ~NC_SESSION_CALLHOME;
3132 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003133
Michal Vasko3f05a092018-03-13 10:39:49 +01003134 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003135}
3136
3137static void *
3138nc_ch_client_thread(void *arg)
3139{
3140 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3141 NC_MSG_TYPE msgtype;
3142 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003143 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003144 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003145 struct nc_ch_endpt *cur_endpt;
3146 struct nc_session *session;
3147 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003148 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003149 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003150
3151 /* LOCK */
3152 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3153 if (!client) {
3154 goto cleanup;
3155 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003156 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003157
3158 cur_endpt = &client->ch_endpts[0];
3159 cur_endpt_name = strdup(cur_endpt->name);
3160
Michal Vasko29af44b2016-10-13 10:59:55 +02003161 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003162 while (1) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02003163 msgtype = nc_connect_ch_endpt(cur_endpt, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003164
3165 if (msgtype == NC_MSG_HELLO) {
3166 /* UNLOCK */
3167 nc_server_ch_client_unlock(client);
3168
Michal Vasko29af44b2016-10-13 10:59:55 +02003169 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003170 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3171 goto cleanup;
3172 }
Michal Vasko29af44b2016-10-13 10:59:55 +02003173 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003174
3175 /* LOCK */
3176 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3177 if (!client) {
3178 goto cleanup;
3179 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003180 if (client->id != client_id) {
3181 nc_server_ch_client_unlock(client);
3182 goto cleanup;
3183 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003184
3185 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003186 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003187 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003188 /* UNLOCK */
3189 nc_server_ch_client_unlock(client);
3190
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003191 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
3192 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3193 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003194
3195 /* LOCK */
3196 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3197 if (!client) {
3198 goto cleanup;
3199 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003200 if (client->id != client_id) {
3201 nc_server_ch_client_unlock(client);
3202 goto cleanup;
3203 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003204 }
3205
3206 /* set next endpoint to try */
3207 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003208 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003209 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003210 /* we keep the current one but due to unlock/lock we have to find it again */
3211 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3212 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3213 break;
3214 }
3215 }
3216 if (next_endpt_index >= client->ch_endpt_count) {
3217 /* endpoint was removed, start with the first one */
3218 next_endpt_index = 0;
3219 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003220 } else {
3221 /* just get a random index */
3222 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003223 }
3224
Michal Vasko2e6defd2016-10-07 15:48:15 +02003225 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003226 /* UNLOCK */
3227 nc_server_ch_client_unlock(client);
3228
Michal Vasko2e6defd2016-10-07 15:48:15 +02003229 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003230 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3231
Michal Vasko6bb116b2016-10-26 13:53:46 +02003232 /* LOCK */
3233 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3234 if (!client) {
3235 goto cleanup;
3236 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003237 if (client->id != client_id) {
3238 nc_server_ch_client_unlock(client);
3239 goto cleanup;
3240 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003241
Michal Vasko2e6defd2016-10-07 15:48:15 +02003242 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003243
3244 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003245 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3246 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003247 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003248 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003249 }
3250
Peter Feiged05f2252018-09-03 08:09:47 +00003251 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003252 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003253 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003254 cur_attempts = 0;
3255 } else if (cur_attempts == client->max_attempts) {
3256 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003257 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003258 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003259 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003260 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003261 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003262 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003263 }
3264
3265 cur_attempts = 0;
3266 } /* else we keep the current one */
3267 }
Peter Feiged05f2252018-09-03 08:09:47 +00003268
3269 cur_endpt = &client->ch_endpts[next_endpt_index];
3270 free(cur_endpt_name);
3271 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003272 }
3273
3274cleanup:
3275 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003276 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003277 free(data->client_name);
3278 free(data);
3279 return NULL;
3280}
3281
3282API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02003283nc_connect_ch_client_dispatch(const char *client_name, void (*session_clb)(const char *client_name,
3284 struct nc_session *new_session))
Michal Vasko3f05a092018-03-13 10:39:49 +01003285{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003286 int ret;
3287 pthread_t tid;
3288 struct nc_ch_client_thread_arg *arg;
3289
3290 if (!client_name) {
3291 ERRARG("client_name");
3292 return -1;
3293 } else if (!session_clb) {
3294 ERRARG("session_clb");
3295 return -1;
3296 }
3297
3298 arg = malloc(sizeof *arg);
3299 if (!arg) {
3300 ERRMEM;
3301 return -1;
3302 }
3303 arg->client_name = strdup(client_name);
3304 if (!arg->client_name) {
3305 ERRMEM;
3306 free(arg);
3307 return -1;
3308 }
3309 arg->session_clb = session_clb;
3310
3311 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3312 if (ret) {
3313 ERR("Creating a new thread failed (%s).", strerror(ret));
3314 free(arg->client_name);
3315 free(arg);
3316 return -1;
3317 }
3318 /* the thread now manages arg */
3319
3320 pthread_detach(tid);
3321
3322 return 0;
3323}
3324
Radek Krejci53691be2016-02-22 13:58:37 +01003325#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003326
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003327API time_t
3328nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003329{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003330 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003331 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003332 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003333 }
3334
Michal Vasko2e6defd2016-10-07 15:48:15 +02003335 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003336}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003337
3338API void
3339nc_session_set_notif_status(struct nc_session *session, int notif_status)
3340{
3341 if (!session || (session->side != NC_SERVER)) {
3342 ERRARG("session");
3343 return;
3344 }
3345
3346 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3347}
3348
3349API int
3350nc_session_get_notif_status(const struct nc_session *session)
3351{
3352 if (!session || (session->side != NC_SERVER)) {
3353 ERRARG("session");
3354 return 0;
3355 }
3356
3357 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003358}
Michal Vasko8f430592019-02-26 08:32:54 +01003359
3360API int
3361nc_session_is_callhome(const struct nc_session *session)
3362{
3363 if (!session || (session->side != NC_SERVER)) {
3364 ERRARG("session");
3365 return 0;
3366 }
3367
3368 if (session->flags & NC_SESSION_CALLHOME) {
3369 return 1;
3370 }
3371
3372 return 0;
3373}