blob: 3b747ed0a251fa3b88350a4a077be900c5aaec56 [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"
36
Michal Vaskob48aa812016-01-18 14:13:09 +010037struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010038#ifdef NC_ENABLED_SSH
39 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
40#endif
41 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020042 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
43 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010044};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010045
fanchanghu966f2de2016-07-21 02:28:57 -040046static nc_rpc_clb global_rpc_clb = NULL;
47
Michal Vasko3031aae2016-01-27 16:07:18 +010048struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010049nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010050{
51 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010052 struct nc_endpt *endpt = NULL;
53
Michal Vaskoade892d2017-02-22 13:40:35 +010054 /* WRITE LOCK */
55 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010056
57 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020058 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010059 endpt = &server_opts.endpts[i];
60 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010061 }
62 }
63
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010064 if (!endpt) {
65 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010066 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020067 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010068 return NULL;
69 }
70
Michal Vaskoe2713da2016-08-22 16:06:40 +020071 if (idx) {
72 *idx = i;
73 }
74
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010075 return endpt;
76}
77
Michal Vasko2e6defd2016-10-07 15:48:15 +020078struct nc_ch_client *
79nc_server_ch_client_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010080{
Michal Vasko2e6defd2016-10-07 15:48:15 +020081 uint16_t i;
82 struct nc_ch_client *client = NULL;
83
84 /* READ LOCK */
85 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
86
87 for (i = 0; i < server_opts.ch_client_count; ++i) {
88 if (!strcmp(server_opts.ch_clients[i].name, name) && (!ti || (server_opts.ch_clients[i].ti == ti))) {
89 client = &server_opts.ch_clients[i];
90 break;
91 }
92 }
93
94 if (!client) {
95 ERR("Call Home client \"%s\" was not found.", name);
96 /* READ UNLOCK */
97 pthread_rwlock_unlock(&server_opts.ch_client_lock);
98 return NULL;
99 }
100
101 /* CH CLIENT LOCK */
102 pthread_mutex_lock(&client->lock);
103
104 if (idx) {
105 *idx = i;
106 }
107
108 return client;
109}
110
111void
112nc_server_ch_client_unlock(struct nc_ch_client *client)
113{
114 /* CH CLIENT UNLOCK */
115 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100116
117 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200118 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100119}
Michal Vasko086311b2016-01-08 09:53:11 +0100120
Michal Vasko1a38c862016-01-15 15:50:07 +0100121API void
122nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
123{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200124 if (!session) {
125 ERRARG("session");
126 return;
127 } else if (!reason) {
128 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100129 return;
130 }
131
Michal Vasko142cfea2017-08-07 10:12:11 +0200132 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
133 session->killed_by = 0;
134 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100135 session->term_reason = reason;
136}
137
Michal Vasko142cfea2017-08-07 10:12:11 +0200138API void
139nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
140{
141 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
142 ERRARG("session");
143 return;
144 } else if (!sid) {
145 ERRARG("sid");
146 return;
147 }
148
149 session->killed_by = sid;
150}
151
152API void
153nc_session_set_status(struct nc_session *session, NC_STATUS status)
154{
155 if (!session) {
156 ERRARG("session");
157 return;
158 } else if (!status) {
159 ERRARG("status");
160 return;
161 }
162
163 session->status = status;
164}
165
Michal Vasko086311b2016-01-08 09:53:11 +0100166int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200167nc_sock_listen_inet(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100168{
Michal Vasko06c860d2018-07-09 16:08:52 +0200169 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100170 int is_ipv4, sock;
171 struct sockaddr_storage saddr;
172
173 struct sockaddr_in *saddr4;
174 struct sockaddr_in6 *saddr6;
175
176
177 if (!strchr(address, ':')) {
178 is_ipv4 = 1;
179 } else {
180 is_ipv4 = 0;
181 }
182
183 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
184 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100185 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100186 goto fail;
187 }
188
Michal Vaskobe52dc22018-10-17 09:28:17 +0200189 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200190 opt = 1;
191 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
192 ERR("Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
193 goto fail;
194 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100195 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
196 ERR("Could not set TCP_NODELAY socket option (%s).", strerror(errno));
197 goto fail;
198 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200199
200 if (nc_sock_enable_keepalive(sock)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100201 goto fail;
202 }
203
204 bzero(&saddr, sizeof(struct sockaddr_storage));
205 if (is_ipv4) {
206 saddr4 = (struct sockaddr_in *)&saddr;
207
208 saddr4->sin_family = AF_INET;
209 saddr4->sin_port = htons(port);
210
211 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100212 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100213 goto fail;
214 }
215
216 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100217 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100218 goto fail;
219 }
220
221 } else {
222 saddr6 = (struct sockaddr_in6 *)&saddr;
223
224 saddr6->sin6_family = AF_INET6;
225 saddr6->sin6_port = htons(port);
226
227 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100228 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100229 goto fail;
230 }
231
232 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100233 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100234 goto fail;
235 }
236 }
237
Michal Vaskofb89d772016-01-08 12:25:35 +0100238 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100239 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100240 goto fail;
241 }
242
243 return sock;
244
245fail:
246 if (sock > -1) {
247 close(sock);
248 }
249
250 return -1;
251}
252
253int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200254nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
255{
256 struct sockaddr_un sun;
257 int sock = -1;
258
259 sock = socket(AF_UNIX, SOCK_STREAM, 0);
260 if (sock == -1) {
261 ERR("Failed to create socket (%s).", strerror(errno));
262 goto fail;
263 }
264
265 memset(&sun, 0, sizeof(sun));
266 sun.sun_family = AF_UNIX;
267 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
268
269 unlink(sun.sun_path);
270 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
271 ERR("Could not bind \"%s\" (%s).", address, strerror(errno));
272 goto fail;
273 }
274
275 if (opts->mode != (mode_t)-1) {
276 if (chmod(sun.sun_path, opts->mode) < 0) {
277 ERR("Failed to set unix socket permissions (%s).", strerror(errno));
278 goto fail;
279 }
280 }
281
282 if (opts->uid != (uid_t)-1 || opts->gid != (gid_t)-1) {
283 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
284 ERR("Failed to set unix socket uid/gid (%s).", strerror(errno));
285 goto fail;
286 }
287 }
288
289 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
290 ERR("Unable to start listening on \"%s\" (%s).", address, strerror(errno));
291 goto fail;
292 }
293
294 return sock;
295
296fail:
297 if (sock > -1) {
298 close(sock);
299 }
300
301 return -1;
302}
303
304int
Michal Vasko3031aae2016-01-27 16:07:18 +0100305nc_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 +0100306{
Michal Vaskof54cd352017-02-22 13:42:02 +0100307 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100308 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100309 struct pollfd *pfd;
310 struct sockaddr_storage saddr;
311 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100312 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100313
314 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100315 if (!pfd) {
316 ERRMEM;
317 return -1;
318 }
319
Michal Vaskoac2f6182017-01-30 14:32:03 +0100320 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200321 if (binds[i].sock < 0) {
322 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200323 continue;
324 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200325 if (binds[i].pollin) {
326 binds[i].pollin = 0;
327 /* leftover pollin */
328 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100329 break;
330 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100331 pfd[pfd_count].fd = binds[i].sock;
332 pfd[pfd_count].events = POLLIN;
333 pfd[pfd_count].revents = 0;
334
335 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100336 }
337
Michal Vasko0a3f3752016-10-13 14:58:38 +0200338 if (sock == -1) {
339 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100340 sigfillset(&sigmask);
341 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100342 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100343 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
344
Michal Vasko0a3f3752016-10-13 14:58:38 +0200345 if (!ret) {
346 /* we timeouted */
347 free(pfd);
348 return 0;
349 } else if (ret == -1) {
350 ERR("Poll failed (%s).", strerror(errno));
351 free(pfd);
352 return -1;
353 }
Michal Vasko086311b2016-01-08 09:53:11 +0100354
Michal Vaskoac2f6182017-01-30 14:32:03 +0100355 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
356 /* adjust i so that indices in binds and pfd always match */
357 while (binds[i].sock != pfd[j].fd) {
358 ++i;
359 }
360
361 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200362 --ret;
363
364 if (!ret) {
365 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100366 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200367 break;
368 } else {
369 /* just remember the event for next time */
370 binds[i].pollin = 1;
371 }
372 }
Michal Vasko086311b2016-01-08 09:53:11 +0100373 }
374 }
375 free(pfd);
376
377 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100378 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100379 return -1;
380 }
381
382 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100383 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100384 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100385 return -1;
386 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200387 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100388
Michal Vasko0190bc32016-03-02 15:47:49 +0100389 /* make the socket non-blocking */
390 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
391 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100392 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100393 return -1;
394 }
395
Michal Vasko3031aae2016-01-27 16:07:18 +0100396 if (idx) {
397 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100398 }
399
Michal Vasko086311b2016-01-08 09:53:11 +0100400 /* host was requested */
401 if (host) {
402 if (saddr.ss_family == AF_INET) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000403 *host = malloc(INET_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100404 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000405 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100406 ERR("inet_ntop failed (%s).", strerror(errno));
407 free(*host);
408 *host = NULL;
409 }
Michal Vasko086311b2016-01-08 09:53:11 +0100410
Michal Vasko4eb3c312016-03-01 14:09:37 +0100411 if (port) {
412 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
413 }
414 } else {
415 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100416 }
417 } else if (saddr.ss_family == AF_INET6) {
Jan Kundrát0f942e82018-02-14 14:52:00 +0100418 *host = malloc(INET6_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100419 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000420 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100421 ERR("inet_ntop failed (%s).", strerror(errno));
422 free(*host);
423 *host = NULL;
424 }
Michal Vasko086311b2016-01-08 09:53:11 +0100425
Michal Vasko4eb3c312016-03-01 14:09:37 +0100426 if (port) {
427 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
428 }
429 } else {
430 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100431 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200432 } else if (saddr.ss_family == AF_UNIX) {
433 *host = strdup(((struct sockaddr_un *)&saddr)->sun_path);
434 if (*host) {
435 if (port) {
436 *port = 0;
437 }
438 } else {
439 ERRMEM;
440 }
Michal Vasko086311b2016-01-08 09:53:11 +0100441 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100442 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100443 }
444 }
445
446 return ret;
447}
448
Michal Vasko05ba9df2016-01-13 14:40:27 +0100449static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100450nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100451{
452 const char *identifier = NULL, *version = NULL, *format = NULL;
453 char *model_data = NULL;
454 const struct lys_module *module;
455 struct nc_server_error *err;
456 struct lyd_node *child, *data = NULL;
Michal Vasko88639e92017-08-03 14:38:10 +0200457 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100458
459 LY_TREE_FOR(rpc->child, child) {
460 if (!strcmp(child->schema->name, "identifier")) {
461 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
462 } else if (!strcmp(child->schema->name, "version")) {
463 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500464 if (version && version[0] == '\0') {
465 version = NULL;
466 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100467 } else if (!strcmp(child->schema->name, "format")) {
468 format = ((struct lyd_node_leaf_list *)child)->value_str;
469 }
470 }
471
472 /* check version */
473 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100474 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
475 nc_err_set_msg(err, "The requested version is not supported.", "en");
476 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100477 }
478
479 /* check and get module with the name identifier */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200480 module = ly_ctx_get_module(server_opts.ctx, identifier, version, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100481 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200482 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
483 }
484 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100485 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
486 nc_err_set_msg(err, "The requested schema was not found.", "en");
487 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100488 }
489
490 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100491 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100492 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL, 0, 0);
Radek Krejci90fba642016-12-07 15:59:45 +0100493 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100494 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100495 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100496 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
497 nc_err_set_msg(err, "The requested format is not supported.", "en");
498 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100499 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200500 if (!model_data) {
501 ERRINT;
502 return NULL;
503 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100504
Michal Vasko88639e92017-08-03 14:38:10 +0200505 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
506 if (!sdata) {
507 ERRINT;
508 free(model_data);
509 return NULL;
510 }
511
Radek Krejci539efb62016-08-24 15:05:16 +0200512 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
513 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100514 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100515 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200516 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100517 return NULL;
518 }
519
Radek Krejci36dfdb32016-09-01 16:56:35 +0200520 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100521}
522
523static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100524nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100525{
Michal Vasko428087d2016-01-14 16:04:28 +0100526 session->term_reason = NC_SESSION_TERM_CLOSED;
527 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100528}
529
Michal Vasko086311b2016-01-08 09:53:11 +0100530API int
531nc_server_init(struct ly_ctx *ctx)
532{
Michal Vasko88639e92017-08-03 14:38:10 +0200533 const struct lys_node *rpc;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000534 pthread_rwlockattr_t attr;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100535
Michal Vasko086311b2016-01-08 09:53:11 +0100536 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200537 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100538 return -1;
539 }
540
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100541 nc_init();
542
Michal Vasko05ba9df2016-01-13 14:40:27 +0100543 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200544 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
545 if (rpc && !rpc->priv) {
546 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100547 }
548
549 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200550 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
551 if (rpc && !rpc->priv) {
552 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100553 }
554
Michal Vasko086311b2016-01-08 09:53:11 +0100555 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100556
557 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500558 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100559
Frank Rimpler9f838b02018-07-25 06:44:03 +0000560 errno=0;
561
562 if (pthread_rwlockattr_init(&attr) == 0) {
Rosen Penev153fe402019-07-15 18:15:28 -0700563#if defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
Frank Rimpler9f838b02018-07-25 06:44:03 +0000564 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
565 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
566 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
567 }
568 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
569 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
570 }
571 } else {
572 ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
573 }
Rosen Penev153fe402019-07-15 18:15:28 -0700574#endif
Frank Rimpler9f838b02018-07-25 06:44:03 +0000575 pthread_rwlockattr_destroy(&attr);
576 } else {
577 ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
578 }
Michal Vasko086311b2016-01-08 09:53:11 +0100579 return 0;
580}
581
Michal Vaskob48aa812016-01-18 14:13:09 +0100582API void
583nc_server_destroy(void)
584{
Radek Krejci658782b2016-12-04 22:04:55 +0100585 unsigned int i;
586
587 for (i = 0; i < server_opts.capabilities_count; i++) {
588 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
589 }
590 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200591 server_opts.capabilities = NULL;
592 server_opts.capabilities_count = 0;
593
Radek Krejci53691be2016-02-22 13:58:37 +0100594#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100595 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100596#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100597#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100598 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
599 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
600 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200601 server_opts.passwd_auth_data = NULL;
602 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100603
Michal Vasko17dfda92016-12-01 14:06:16 +0100604 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100605
606 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
607 server_opts.hostkey_data_free(server_opts.hostkey_data);
608 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200609 server_opts.hostkey_data = NULL;
610 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100611#endif
612#ifdef NC_ENABLED_TLS
613 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
614 server_opts.server_cert_data_free(server_opts.server_cert_data);
615 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200616 server_opts.server_cert_data = NULL;
617 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100618 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
619 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
620 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200621 server_opts.trusted_cert_list_data = NULL;
622 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100623#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100624 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100625}
626
Michal Vasko086311b2016-01-08 09:53:11 +0100627API int
628nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
629{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200630 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
631 ERRARG("basic_mode");
632 return -1;
633 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
634 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100635 return -1;
636 }
637
638 server_opts.wd_basic_mode = basic_mode;
639 server_opts.wd_also_supported = also_supported;
640 return 0;
641}
642
Michal Vasko1a38c862016-01-15 15:50:07 +0100643API void
Michal Vasko55f03972016-04-13 08:56:01 +0200644nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
645{
646 if (!basic_mode && !also_supported) {
647 ERRARG("basic_mode and also_supported");
648 return;
649 }
650
651 if (basic_mode) {
652 *basic_mode = server_opts.wd_basic_mode;
653 }
654 if (also_supported) {
655 *also_supported = server_opts.wd_also_supported;
656 }
657}
658
Michal Vasko55f03972016-04-13 08:56:01 +0200659API int
Radek Krejci658782b2016-12-04 22:04:55 +0100660nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200661{
Radek Krejci658782b2016-12-04 22:04:55 +0100662 const char **new;
663
664 if (!value || !value[0]) {
665 ERRARG("value must not be empty");
666 return EXIT_FAILURE;
667 }
668
669 server_opts.capabilities_count++;
670 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
671 if (!new) {
672 ERRMEM;
673 return EXIT_FAILURE;
674 }
675 server_opts.capabilities = new;
676 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
677
678 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200679}
680
Michal Vasko1a38c862016-01-15 15:50:07 +0100681API void
Michal Vasko086311b2016-01-08 09:53:11 +0100682nc_server_set_hello_timeout(uint16_t hello_timeout)
683{
Michal Vasko086311b2016-01-08 09:53:11 +0100684 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100685}
686
Michal Vasko55f03972016-04-13 08:56:01 +0200687API uint16_t
688nc_server_get_hello_timeout(void)
689{
690 return server_opts.hello_timeout;
691}
692
Michal Vasko1a38c862016-01-15 15:50:07 +0100693API void
Michal Vasko086311b2016-01-08 09:53:11 +0100694nc_server_set_idle_timeout(uint16_t idle_timeout)
695{
Michal Vasko086311b2016-01-08 09:53:11 +0100696 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100697}
698
Michal Vasko55f03972016-04-13 08:56:01 +0200699API uint16_t
700nc_server_get_idle_timeout(void)
701{
702 return server_opts.idle_timeout;
703}
704
Michal Vasko71090fc2016-05-24 16:37:28 +0200705API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100706nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100707{
Michal Vasko71090fc2016-05-24 16:37:28 +0200708 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200709 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200710
Michal Vasko45e53ae2016-04-07 11:46:03 +0200711 if (!server_opts.ctx) {
712 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200713 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200714 } else if (fdin < 0) {
715 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200716 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200717 } else if (fdout < 0) {
718 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200719 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200720 } else if (!username) {
721 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200722 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200723 } else if (!session) {
724 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200725 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100726 }
727
728 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200729 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100730 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100731 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200732 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100733 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100734 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100735
Michal Vasko086311b2016-01-08 09:53:11 +0100736 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100737 (*session)->ti_type = NC_TI_FD;
738 (*session)->ti.fd.in = fdin;
739 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100740
741 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100742 (*session)->flags = NC_SESSION_SHAREDCTX;
743 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100744
Michal Vaskob48aa812016-01-18 14:13:09 +0100745 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +0100746 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100747
Michal Vasko086311b2016-01-08 09:53:11 +0100748 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200749 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200750 if (msgtype != NC_MSG_HELLO) {
751 nc_session_free(*session, NULL);
752 *session = NULL;
753 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100754 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200755
756 nc_gettimespec_mono(&ts_cur);
757 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
758 nc_gettimespec_real(&ts_cur);
759 (*session)->opts.server.session_start = ts_cur.tv_sec;
760
Michal Vasko1a38c862016-01-15 15:50:07 +0100761 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100762
Michal Vasko71090fc2016-05-24 16:37:28 +0200763 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100764}
Michal Vasko9e036d52016-01-08 10:49:26 +0100765
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200766static int
767nc_accept_unix(struct nc_session *session, int sock)
768{
769 const struct passwd *pw;
770 struct ucred ucred;
771 char *username;
772 socklen_t len;
773
774 session->ti_type = NC_TI_UNIX;
775
776 len = sizeof(ucred);
777 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
778 ERR("Failed to get credentials from unix socket (%s).",
779 strerror(errno));
780 close(sock);
781 return -1;
782 }
783
784 pw = getpwuid(ucred.uid);
785 if (pw == NULL) {
786 ERR("Failed to find username for uid=%u (%s).\n", ucred.uid,
787 strerror(errno));
788 close(sock);
789 return -1;
790 }
791
792 username = strdup(pw->pw_name);
793 if (username == NULL) {
794 ERRMEM;
795 close(sock);
796 return -1;
797 }
798 session->username = lydict_insert_zc(server_opts.ctx, username);
799
800 session->ti.unixsock.sock = sock;
801
802 return 1;
803}
804
Michal Vaskob30b99c2016-07-26 11:35:43 +0200805static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100806nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
807{
808 uint8_t q_last;
809
810 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
811 ERRINT;
812 return;
813 }
814
815 /* get a unique queue value (by adding 1 to the last added value, if any) */
816 if (ps->queue_len) {
817 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
818 *id = ps->queue[q_last] + 1;
819 } else {
820 *id = 0;
821 }
822
823 /* add the id into the queue */
824 ++ps->queue_len;
825 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
826 ps->queue[q_last] = *id;
827}
828
829static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200830nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
831{
Michal Vasko74c345f2018-02-07 10:37:11 +0100832 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200833
834 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100835 /* get the actual queue idx */
836 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200837
838 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100839 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200840 /* another equal value, simply cannot be */
841 ERRINT;
842 }
Michal Vaskod8340032018-02-12 14:41:00 +0100843 if (found == 2) {
844 /* move the following values */
845 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
846 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100847 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200848 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100849 if (i == 0) {
850 found = 1;
851 } else {
852 /* this is not okay, our id is in the middle of the queue */
853 found = 2;
854 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200855 }
856 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200857 if (!found) {
858 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100859 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200860 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100861
Michal Vasko103fe632018-02-12 16:37:45 +0100862 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100863 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100864 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100865 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
866 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200867}
868
Michal Vaskof04a52a2016-04-07 10:52:10 +0200869int
Michal Vasko26043172016-07-26 14:08:59 +0200870nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200871{
872 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200873 struct timespec ts;
874
Michal Vasko77a6abe2017-10-05 10:02:20 +0200875 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100876 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200877
878 /* LOCK */
879 ret = pthread_mutex_timedlock(&ps->lock, &ts);
880 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200881 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200882 return -1;
883 }
884
Michal Vasko74c345f2018-02-07 10:37:11 +0100885 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100886 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100887 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100888 pthread_mutex_unlock(&ps->lock);
889 return -1;
890 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100891
892 /* add ourselves into the queue */
893 nc_ps_queue_add_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200894
895 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200896 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200897 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100898 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200899
900 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
901 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +0530902 /**
903 * This may happen when another thread releases the lock and broadcasts the condition
904 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
905 * but when actually this thread was ready for condition.
906 */
preetbhansali629dfc42018-12-17 16:04:40 +0530907 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +0530908 break;
909 }
Michal Vasko66032bc2019-01-22 15:03:12 +0100910
Michal Vasko26043172016-07-26 14:08:59 +0200911 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200912 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200913 nc_ps_queue_remove_id(ps, *id);
914 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200915 return -1;
916 }
917 }
918
Michal Vaskobe86fe32016-04-07 10:43:03 +0200919 /* UNLOCK */
920 pthread_mutex_unlock(&ps->lock);
921
922 return 0;
923}
924
Michal Vaskof04a52a2016-04-07 10:52:10 +0200925int
Michal Vasko26043172016-07-26 14:08:59 +0200926nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200927{
928 int ret;
929 struct timespec ts;
930
Michal Vasko77a6abe2017-10-05 10:02:20 +0200931 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100932 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200933
934 /* LOCK */
935 ret = pthread_mutex_timedlock(&ps->lock, &ts);
936 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200937 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200938 ret = -1;
939 }
940
Michal Vaskob30b99c2016-07-26 11:35:43 +0200941 /* we must be the first, it was our turn after all, right? */
942 if (ps->queue[ps->queue_begin] != id) {
943 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200944 /* UNLOCK */
945 if (!ret) {
946 pthread_mutex_unlock(&ps->lock);
947 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200948 return -1;
949 }
950
Michal Vaskobe86fe32016-04-07 10:43:03 +0200951 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200952 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200953
954 /* broadcast to all other threads that the queue moved */
955 pthread_cond_broadcast(&ps->cond);
956
Michal Vaskobe86fe32016-04-07 10:43:03 +0200957 /* UNLOCK */
958 if (!ret) {
959 pthread_mutex_unlock(&ps->lock);
960 }
961
962 return ret;
963}
964
Michal Vasko428087d2016-01-14 16:04:28 +0100965API struct nc_pollsession *
966nc_ps_new(void)
967{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100968 struct nc_pollsession *ps;
969
970 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100971 if (!ps) {
972 ERRMEM;
973 return NULL;
974 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200975 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100976 pthread_mutex_init(&ps->lock, NULL);
977
978 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100979}
980
981API void
982nc_ps_free(struct nc_pollsession *ps)
983{
fanchanghu3d4e7212017-08-09 09:42:30 +0800984 uint16_t i;
985
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100986 if (!ps) {
987 return;
988 }
989
Michal Vaskobe86fe32016-04-07 10:43:03 +0200990 if (ps->queue_len) {
991 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
992 }
993
fanchanghu3d4e7212017-08-09 09:42:30 +0800994 for (i = 0; i < ps->session_count; i++) {
995 free(ps->sessions[i]);
996 }
997
Michal Vasko428087d2016-01-14 16:04:28 +0100998 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100999 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001000 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001001
Michal Vasko428087d2016-01-14 16:04:28 +01001002 free(ps);
1003}
1004
1005API int
1006nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1007{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001008 uint8_t q_id;
1009
Michal Vasko45e53ae2016-04-07 11:46:03 +02001010 if (!ps) {
1011 ERRARG("ps");
1012 return -1;
1013 } else if (!session) {
1014 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001015 return -1;
1016 }
1017
Michal Vasko48a63ed2016-03-01 09:48:21 +01001018 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001019 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001020 return -1;
1021 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001022
Michal Vasko428087d2016-01-14 16:04:28 +01001023 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001024 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001025 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001026 ERRMEM;
1027 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001028 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001029 return -1;
1030 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001031 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1032 if (!ps->sessions[ps->session_count - 1]) {
1033 ERRMEM;
1034 --ps->session_count;
1035 /* UNLOCK */
1036 nc_ps_unlock(ps, q_id, __func__);
1037 return -1;
1038 }
1039 ps->sessions[ps->session_count - 1]->session = session;
1040 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001041
Michal Vasko48a63ed2016-03-01 09:48:21 +01001042 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001043 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001044}
1045
Michal Vasko48a63ed2016-03-01 09:48:21 +01001046static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001047_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001048{
1049 uint16_t i;
1050
Radek Krejcid5f978f2016-03-03 13:14:45 +01001051 if (index >= 0) {
1052 i = (uint16_t)index;
1053 goto remove;
1054 }
Michal Vasko428087d2016-01-14 16:04:28 +01001055 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001056 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001057remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001058 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001059 if (i <= ps->session_count) {
1060 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001061 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001062 }
1063 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001064 free(ps->sessions);
1065 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001066 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001067 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001068 return 0;
1069 }
1070 }
1071
Michal Vaskof0537d82016-01-29 14:42:38 +01001072 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001073}
1074
Michal Vasko48a63ed2016-03-01 09:48:21 +01001075API int
1076nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1077{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001078 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001079 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001080
Michal Vasko45e53ae2016-04-07 11:46:03 +02001081 if (!ps) {
1082 ERRARG("ps");
1083 return -1;
1084 } else if (!session) {
1085 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001086 return -1;
1087 }
1088
1089 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001090 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001091 return -1;
1092 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001093
Radek Krejcid5f978f2016-03-03 13:14:45 +01001094 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001095
1096 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001097 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001098
Michal Vaskobe86fe32016-04-07 10:43:03 +02001099 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001100}
1101
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001102API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001103nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001104{
1105 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001106 struct nc_session *ret = NULL;
1107
1108 if (!ps) {
1109 ERRARG("ps");
1110 return NULL;
1111 }
1112
1113 /* LOCK */
1114 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1115 return NULL;
1116 }
1117
Michal Vasko4871c9d2017-10-09 14:48:39 +02001118 if (idx < ps->session_count) {
1119 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001120 }
1121
1122 /* UNLOCK */
1123 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1124
1125 return ret;
1126}
1127
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001128API uint16_t
1129nc_ps_session_count(struct nc_pollsession *ps)
1130{
Michal Vasko47003942019-03-14 12:25:23 +01001131 uint8_t q_id;
1132 uint16_t session_count;
1133
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001134 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001135 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001136 return 0;
1137 }
1138
Michal Vasko47003942019-03-14 12:25:23 +01001139 /* LOCK (just for memory barrier so that we read the current value) */
1140 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1141 return 0;
1142 }
1143
1144 session_count = ps->session_count;
1145
1146 /* UNLOCK */
1147 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1148
1149 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001150}
1151
Michal Vasko131120a2018-05-29 15:44:02 +02001152/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001153 * returns: NC_PSPOLL_ERROR,
1154 * NC_PSPOLL_BAD_RPC,
1155 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1156 * NC_PSPOLL_RPC
1157 */
1158static int
Michal Vasko131120a2018-05-29 15:44:02 +02001159nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001160{
1161 struct lyxml_elem *xml = NULL;
1162 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001163 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001164 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001165
Michal Vasko45e53ae2016-04-07 11:46:03 +02001166 if (!session) {
1167 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001168 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001169 } else if (!rpc) {
1170 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001171 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001172 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001173 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001174 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001175 }
1176
Michal Vasko131120a2018-05-29 15:44:02 +02001177 msgtype = nc_read_msg_io(session, io_timeout, &xml, 0);
Michal Vasko428087d2016-01-14 16:04:28 +01001178
1179 switch (msgtype) {
1180 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001181 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001182 if (!*rpc) {
1183 ERRMEM;
1184 goto error;
1185 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001186
Radek Krejcif93c7d42016-04-06 13:41:15 +02001187 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001188 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1189 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001190 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001191 /* parsing RPC failed */
Michal Vaskoc9970242018-02-14 16:03:35 +01001192 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
Michal Vasko131120a2018-05-29 15:44:02 +02001193 ret = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001194 nc_server_reply_free(reply);
1195 if (ret == -1) {
1196 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001197 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001198 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1199 } else {
1200 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001201 }
Michal Vasko428087d2016-01-14 16:04:28 +01001202 (*rpc)->root = xml;
1203 break;
1204 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001205 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001206 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001207 goto error;
1208 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001209 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001210 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001211 goto error;
1212 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001213 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001214 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001215 goto error;
1216 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001217 /* NC_MSG_ERROR,
Michal Vasko131120a2018-05-29 15:44:02 +02001218 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg_io()
Michal Vasko428087d2016-01-14 16:04:28 +01001219 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001220 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001221 break;
1222 }
1223
Michal Vasko71090fc2016-05-24 16:37:28 +02001224 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001225
1226error:
1227 /* cleanup */
1228 lyxml_free(server_opts.ctx, xml);
1229
Michal Vasko71090fc2016-05-24 16:37:28 +02001230 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001231}
1232
fanchanghu966f2de2016-07-21 02:28:57 -04001233API void
1234nc_set_global_rpc_clb(nc_rpc_clb clb)
1235{
1236 global_rpc_clb = clb;
1237}
1238
Radek Krejci93e80222016-10-03 13:34:25 +02001239API NC_MSG_TYPE
1240nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1241{
Michal Vasko131120a2018-05-29 15:44:02 +02001242 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001243
1244 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001245 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001246 ERRARG("session");
1247 return NC_MSG_ERROR;
1248 } else if (!notif || !notif->tree || !notif->eventtime) {
1249 ERRARG("notif");
1250 return NC_MSG_ERROR;
1251 }
1252
Michal Vasko131120a2018-05-29 15:44:02 +02001253 /* we do not need RPC lock for this, IO lock will be acquired properly */
1254 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
1255 if (ret == NC_MSG_ERROR) {
Radek Krejci93e80222016-10-03 13:34:25 +02001256 ERR("Session %u: failed to write notification.", session->id);
Radek Krejci93e80222016-10-03 13:34:25 +02001257 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001258
Michal Vasko131120a2018-05-29 15:44:02 +02001259 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001260}
1261
Michal Vasko131120a2018-05-29 15:44:02 +02001262/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001263 * returns: NC_PSPOLL_ERROR,
1264 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1265 * NC_PSPOLL_REPLY_ERROR,
1266 * 0
1267 */
1268static int
Michal Vasko131120a2018-05-29 15:44:02 +02001269nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001270{
1271 nc_rpc_clb clb;
1272 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001273 struct lys_node *rpc_act = NULL;
1274 struct lyd_node *next, *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001275 int ret = 0;
1276 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001277
Michal Vasko4a827e52016-03-03 10:59:00 +01001278 if (!rpc) {
1279 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001280 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001281 }
1282
Michal Vasko90e8e692016-07-13 12:27:57 +02001283 if (rpc->tree->schema->nodetype == LYS_RPC) {
1284 /* RPC */
1285 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001286 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001287 /* action */
1288 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1289 if (elem->schema->nodetype == LYS_ACTION) {
1290 rpc_act = elem->schema;
1291 break;
1292 }
1293 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001294 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001295 if (!rpc_act) {
1296 ERRINT;
1297 return NC_PSPOLL_ERROR;
1298 }
1299 }
1300
1301 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001302 if (!global_rpc_clb) {
1303 /* no callback, reply with a not-implemented error */
1304 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
1305 } else {
1306 reply = global_rpc_clb(rpc->tree, session);
1307 }
Michal Vasko428087d2016-01-14 16:04:28 +01001308 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001309 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001310 reply = clb(rpc->tree, session);
1311 }
1312
1313 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001314 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001315 }
Michal Vasko131120a2018-05-29 15:44:02 +02001316 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->root, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001317 if (reply->type == NC_RPL_ERROR) {
1318 ret |= NC_PSPOLL_REPLY_ERROR;
1319 }
1320 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001321
Michal Vasko131120a2018-05-29 15:44:02 +02001322 if (r != NC_MSG_REPLY) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001323 ERR("Session %u: failed to write reply.", session->id);
1324 ret |= NC_PSPOLL_ERROR;
1325 }
Michal Vasko428087d2016-01-14 16:04:28 +01001326
1327 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1328 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1329 session->status = NC_STATUS_INVALID;
1330 }
1331
Michal Vasko71090fc2016-05-24 16:37:28 +02001332 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001333}
1334
Michal Vasko131120a2018-05-29 15:44:02 +02001335/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001336 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1337 * NC_PSPOLL_ERROR, (msg filled)
1338 * NC_PSPOLL_TIMEOUT,
1339 * NC_PSPOLL_RPC (some application data available),
1340 * NC_PSPOLL_SSH_CHANNEL,
1341 * NC_PSPOLL_SSH_MSG
1342 */
1343static int
Michal Vasko131120a2018-05-29 15:44:02 +02001344nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001345{
Michal Vasko9a327362017-01-11 11:31:46 +01001346 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001347 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001348#ifdef NC_ENABLED_SSH
1349 struct nc_session *new;
1350#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001351
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001352 /* check timeout first */
1353 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001354 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001355 sprintf(msg, "session idle timeout elapsed");
1356 session->status = NC_STATUS_INVALID;
1357 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1358 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1359 }
1360
Michal Vasko131120a2018-05-29 15:44:02 +02001361 r = nc_session_io_lock(session, io_timeout, __func__);
1362 if (r < 0) {
1363 sprintf(msg, "session IO lock failed to be acquired");
1364 return NC_PSPOLL_ERROR;
1365 } else if (!r) {
1366 return NC_PSPOLL_TIMEOUT;
1367 }
1368
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001369 switch (session->ti_type) {
1370#ifdef NC_ENABLED_SSH
1371 case NC_TI_LIBSSH:
1372 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001373 if (r == SSH_EOF) {
1374 sprintf(msg, "SSH channel unexpected EOF");
1375 session->status = NC_STATUS_INVALID;
1376 session->term_reason = NC_SESSION_TERM_DROPPED;
1377 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1378 } else if (r == SSH_ERROR) {
1379 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001380 session->status = NC_STATUS_INVALID;
1381 session->term_reason = NC_SESSION_TERM_OTHER;
1382 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001383 } else if (!r) {
1384 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1385 /* new SSH message */
1386 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1387 if (session->ti.libssh.next) {
1388 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1389 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1390 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1391 /* new NETCONF SSH channel */
1392 ret = NC_PSPOLL_SSH_CHANNEL;
1393 break;
1394 }
1395 }
1396 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001397 break;
1398 }
1399 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001400
Michal Vasko8dcaa882017-10-19 14:28:42 +02001401 /* just some SSH message */
1402 ret = NC_PSPOLL_SSH_MSG;
1403 } else {
1404 ret = NC_PSPOLL_TIMEOUT;
1405 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001406 } else {
1407 /* we have some application data */
1408 ret = NC_PSPOLL_RPC;
1409 }
1410 break;
1411#endif
1412#ifdef NC_ENABLED_TLS
1413 case NC_TI_OPENSSL:
1414 r = SSL_pending(session->ti.tls);
1415 if (!r) {
1416 /* no data pending in the SSL buffer, poll fd */
1417 pfd.fd = SSL_get_rfd(session->ti.tls);
1418 if (pfd.fd < 0) {
1419 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1420 ret = NC_PSPOLL_ERROR;
1421 break;
1422 }
1423 pfd.events = POLLIN;
1424 pfd.revents = 0;
1425 r = poll(&pfd, 1, 0);
1426
1427 if ((r < 0) && (errno != EINTR)) {
1428 sprintf(msg, "poll failed (%s)", strerror(errno));
1429 session->status = NC_STATUS_INVALID;
1430 ret = NC_PSPOLL_ERROR;
1431 } else if (r > 0) {
1432 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1433 sprintf(msg, "communication socket unexpectedly closed");
1434 session->status = NC_STATUS_INVALID;
1435 session->term_reason = NC_SESSION_TERM_DROPPED;
1436 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1437 } else if (pfd.revents & POLLERR) {
1438 sprintf(msg, "communication socket error");
1439 session->status = NC_STATUS_INVALID;
1440 session->term_reason = NC_SESSION_TERM_OTHER;
1441 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1442 } else {
1443 ret = NC_PSPOLL_RPC;
1444 }
1445 } else {
1446 ret = NC_PSPOLL_TIMEOUT;
1447 }
1448 } else {
1449 ret = NC_PSPOLL_RPC;
1450 }
1451 break;
1452#endif
1453 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001454 case NC_TI_UNIX:
1455 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001456 pfd.events = POLLIN;
1457 pfd.revents = 0;
1458 r = poll(&pfd, 1, 0);
1459
1460 if ((r < 0) && (errno != EINTR)) {
1461 sprintf(msg, "poll failed (%s)", strerror(errno));
1462 session->status = NC_STATUS_INVALID;
1463 ret = NC_PSPOLL_ERROR;
1464 } else if (r > 0) {
1465 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1466 sprintf(msg, "communication socket unexpectedly closed");
1467 session->status = NC_STATUS_INVALID;
1468 session->term_reason = NC_SESSION_TERM_DROPPED;
1469 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1470 } else if (pfd.revents & POLLERR) {
1471 sprintf(msg, "communication socket error");
1472 session->status = NC_STATUS_INVALID;
1473 session->term_reason = NC_SESSION_TERM_OTHER;
1474 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1475 } else {
1476 ret = NC_PSPOLL_RPC;
1477 }
1478 } else {
1479 ret = NC_PSPOLL_TIMEOUT;
1480 }
1481 break;
1482 case NC_TI_NONE:
1483 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1484 ret = NC_PSPOLL_ERROR;
1485 break;
1486 }
1487
Michal Vasko131120a2018-05-29 15:44:02 +02001488 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001489 return ret;
1490}
1491
1492API int
1493nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1494{
1495 int ret, r;
1496 uint8_t q_id;
1497 uint16_t i, j;
1498 char msg[256];
1499 struct timespec ts_timeout, ts_cur;
1500 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001501 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001502 struct nc_server_rpc *rpc = NULL;
1503
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001504 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001505 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001506 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001507 }
1508
Michal Vaskoade892d2017-02-22 13:40:35 +01001509 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001510 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001511 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001512 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001513
Michal Vaskoade892d2017-02-22 13:40:35 +01001514 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001515 nc_ps_unlock(ps, q_id, __func__);
1516 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001517 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001518
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001519 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001520 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001521 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001522 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001523 nc_addtimespec(&ts_timeout, timeout);
1524 }
1525
1526 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001527 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001528 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001529 if (ps->last_event_session == ps->session_count - 1) {
1530 i = j = 0;
1531 } else {
1532 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001533 }
Michal Vasko9a327362017-01-11 11:31:46 +01001534 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001535 cur_ps_session = ps->sessions[i];
1536 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001537
Michal Vasko131120a2018-05-29 15:44:02 +02001538 /* SESSION RPC LOCK */
1539 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001540 if (r == -1) {
1541 ret = NC_PSPOLL_ERROR;
1542 } else if (r == 1) {
1543 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001544 switch (cur_ps_session->state) {
1545 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001546 if (cur_session->status == NC_STATUS_RUNNING) {
1547 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001548 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001549
Michal Vasko131120a2018-05-29 15:44:02 +02001550 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001551 switch (ret) {
1552 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1553 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001554 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001555 break;
1556 case NC_PSPOLL_ERROR:
1557 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001558 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001559 break;
1560 case NC_PSPOLL_TIMEOUT:
1561#ifdef NC_ENABLED_SSH
1562 case NC_PSPOLL_SSH_CHANNEL:
1563 case NC_PSPOLL_SSH_MSG:
1564#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001565 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001566 break;
1567 case NC_PSPOLL_RPC:
1568 /* let's keep the state busy, we are not done with this session */
1569 break;
1570 }
1571 } else {
1572 /* session is not fine, let the caller know */
1573 ret = NC_PSPOLL_SESSION_TERM;
1574 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1575 ret |= NC_PSPOLL_SESSION_ERROR;
1576 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001577 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001578 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001579 break;
1580 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001581 /* it definitely should not be busy because we have the lock */
1582 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001583 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001584 break;
1585 case NC_PS_STATE_INVALID:
1586 /* we got it locked, but it will be freed, let it be */
1587 ret = NC_PSPOLL_TIMEOUT;
1588 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001589 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001590
Michal Vasko131120a2018-05-29 15:44:02 +02001591 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001592 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001593 /* SESSION RPC UNLOCK */
1594 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001595 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001596 } else {
1597 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001598 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001599 }
Michal Vasko428087d2016-01-14 16:04:28 +01001600
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001601 /* something happened */
1602 if (ret != NC_PSPOLL_TIMEOUT) {
1603 break;
1604 }
1605
Michal Vasko9a327362017-01-11 11:31:46 +01001606 if (i == ps->session_count - 1) {
1607 i = 0;
1608 } else {
1609 ++i;
1610 }
1611 } while (i != j);
1612
Michal Vaskoade892d2017-02-22 13:40:35 +01001613 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001614 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001615 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001616 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001617 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001618
1619 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1620 /* final timeout */
1621 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001622 }
Michal Vasko428087d2016-01-14 16:04:28 +01001623 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001624 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001625
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001626 /* do we want to return the session? */
1627 switch (ret) {
1628 case NC_PSPOLL_RPC:
1629 case NC_PSPOLL_SESSION_TERM:
1630 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1631#ifdef NC_ENABLED_SSH
1632 case NC_PSPOLL_SSH_CHANNEL:
1633 case NC_PSPOLL_SSH_MSG:
1634#endif
1635 if (session) {
1636 *session = cur_session;
1637 }
1638 ps->last_event_session = i;
1639 break;
1640 default:
1641 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001642 }
Michal Vasko428087d2016-01-14 16:04:28 +01001643
Michal Vaskoade892d2017-02-22 13:40:35 +01001644 /* PS UNLOCK */
1645 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001646
Michal Vasko131120a2018-05-29 15:44:02 +02001647 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001648 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001649 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001650 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1651 if (cur_session->status != NC_STATUS_RUNNING) {
1652 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001653 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001654 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001655 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001656 }
1657 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001658 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001659
Michal Vasko7f1ee932018-10-11 09:41:42 +02001660 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001661 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001662 if (cur_session->status != NC_STATUS_RUNNING) {
1663 ret |= NC_PSPOLL_SESSION_TERM;
1664 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1665 ret |= NC_PSPOLL_SESSION_ERROR;
1666 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001667 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001668 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001669 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001670 }
Michal Vasko428087d2016-01-14 16:04:28 +01001671 }
Michal Vasko7f1ee932018-10-11 09:41:42 +02001672 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001673
Michal Vasko131120a2018-05-29 15:44:02 +02001674 /* SESSION RPC UNLOCK */
1675 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001676 }
1677
Michal Vasko48a63ed2016-03-01 09:48:21 +01001678 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001679}
1680
Michal Vaskod09eae62016-02-01 10:32:52 +01001681API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001682nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001683{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001684 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001685 uint16_t i;
1686 struct nc_session *session;
1687
Michal Vasko9a25e932016-02-01 10:36:42 +01001688 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001689 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001690 return;
1691 }
1692
Michal Vasko48a63ed2016-03-01 09:48:21 +01001693 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001694 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001695 return;
1696 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001697
Michal Vasko48a63ed2016-03-01 09:48:21 +01001698 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001699 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001700 nc_session_free(ps->sessions[i]->session, data_free);
1701 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001702 }
1703 free(ps->sessions);
1704 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001705 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001706 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001707 } else {
1708 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001709 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1710 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001711 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001712 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001713 continue;
1714 }
1715
1716 ++i;
1717 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001718 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001719
1720 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001721 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001722}
1723
Radek Krejci53691be2016-02-22 13:58:37 +01001724#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001725
Michal Vaskoe2713da2016-08-22 16:06:40 +02001726API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001727nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001728{
Michal Vasko3031aae2016-01-27 16:07:18 +01001729 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001730 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001731
Michal Vasko45e53ae2016-04-07 11:46:03 +02001732 if (!name) {
1733 ERRARG("name");
1734 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001735 }
1736
Michal Vaskoade892d2017-02-22 13:40:35 +01001737 /* BIND LOCK */
1738 pthread_mutex_lock(&server_opts.bind_lock);
1739
1740 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001741 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001742
1743 /* check name uniqueness */
1744 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001745 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001746 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001747 ret = -1;
1748 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001749 }
1750 }
1751
Michal Vasko3031aae2016-01-27 16:07:18 +01001752 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001753 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001754 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001755 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001756 ret = -1;
1757 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001758 }
1759 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001760 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001761
Michal Vaskoe2713da2016-08-22 16:06:40 +02001762 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001763 if (!server_opts.binds) {
1764 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001765 ret = -1;
1766 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001767 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001768
Michal Vasko2e6defd2016-10-07 15:48:15 +02001769 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1770 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1771 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001772 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001773
1774 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001775#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001776 case NC_TI_LIBSSH:
1777 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1778 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1779 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001780 ret = -1;
1781 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001782 }
1783 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1784 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1785 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1786 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1787 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001788#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001789#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001790 case NC_TI_OPENSSL:
1791 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1792 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1793 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001794 ret = -1;
1795 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001796 }
1797 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001798#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001799 case NC_TI_UNIX:
1800 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1801 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1802 ERRMEM;
1803 ret = -1;
1804 goto cleanup;
1805 }
1806 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1807 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1808 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1809 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001810 default:
1811 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001812 ret = -1;
1813 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001814 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001815
Michal Vaskoade892d2017-02-22 13:40:35 +01001816cleanup:
1817 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001818 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001819
Michal Vaskoade892d2017-02-22 13:40:35 +01001820 /* BIND UNLOCK */
1821 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001822
Michal Vaskoade892d2017-02-22 13:40:35 +01001823 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001824}
1825
Michal Vasko3031aae2016-01-27 16:07:18 +01001826int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001827nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001828{
1829 struct nc_endpt *endpt;
1830 struct nc_bind *bind = NULL;
1831 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001832 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001833
Michal Vasko45e53ae2016-04-07 11:46:03 +02001834 if (!endpt_name) {
1835 ERRARG("endpt_name");
1836 return -1;
1837 } else if ((!address && !port) || (address && port)) {
1838 ERRARG("address and port");
1839 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001840 }
1841
Michal Vaskoe2713da2016-08-22 16:06:40 +02001842 if (address) {
1843 set_addr = 1;
1844 } else {
1845 set_addr = 0;
1846 }
1847
Michal Vaskoade892d2017-02-22 13:40:35 +01001848 /* BIND LOCK */
1849 pthread_mutex_lock(&server_opts.bind_lock);
1850
1851 /* ENDPT LOCK */
1852 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001853 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001854 /* BIND UNLOCK */
1855 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001856 return -1;
1857 }
1858
Michal Vaskoe2713da2016-08-22 16:06:40 +02001859 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001860
Michal Vaskoe2713da2016-08-22 16:06:40 +02001861 if (set_addr) {
1862 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001863 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001864 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001865 }
1866
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001867 if (!set_addr && endpt->ti == NC_TI_UNIX) {
1868 ret = -1;
1869 goto cleanup;
1870 }
1871
Michal Vaskoe2713da2016-08-22 16:06:40 +02001872 /* we have all the information we need to create a listening socket */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001873 if (address && (port || endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001874 /* create new socket, close the old one */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001875 if (endpt->ti == NC_TI_UNIX)
1876 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
1877 else
1878 sock = nc_sock_listen_inet(address, port);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001879 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001880 ret = -1;
1881 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001882 }
1883
1884 if (bind->sock > -1) {
1885 close(bind->sock);
1886 }
1887 bind->sock = sock;
1888 } /* else we are just setting address or port */
1889
1890 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001891 lydict_remove(server_opts.ctx, bind->address);
1892 bind->address = lydict_insert(server_opts.ctx, address, 0);
1893 } else {
1894 bind->port = port;
1895 }
1896
Michal Vaskoe2713da2016-08-22 16:06:40 +02001897 if (sock > -1) {
1898#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001899 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001900#elif defined(NC_ENABLED_SSH)
1901 VRB("Listening on %s:%u for SSH connections.", address, port);
1902#else
1903 VRB("Listening on %s:%u for TLS connections.", address, port);
1904#endif
1905 }
1906
Michal Vasko4c1fb492017-01-30 14:31:07 +01001907cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001908 /* ENDPT UNLOCK */
1909 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001910
Michal Vaskoade892d2017-02-22 13:40:35 +01001911 /* BIND UNLOCK */
1912 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001913
Michal Vasko4c1fb492017-01-30 14:31:07 +01001914 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001915}
1916
Michal Vaskoe2713da2016-08-22 16:06:40 +02001917API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001918nc_server_endpt_set_address(const char *endpt_name, const char *address)
1919{
1920 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1921}
1922
1923API int
1924nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1925{
1926 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1927}
1928
1929API int
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001930nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
1931{
1932 struct nc_endpt *endpt;
1933 uint16_t i;
1934 int ret = 0;
1935
1936 if (!endpt_name) {
1937 ERRARG("endpt_name");
1938 return -1;
1939 } else if (mode == 0) {
1940 ERRARG("mode");
1941 return -1;
1942 }
1943
1944 /* ENDPT LOCK */
1945 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
1946 if (!endpt)
1947 return -1;
1948
1949 if (endpt->ti != NC_TI_UNIX) {
1950 ret = -1;
1951 goto cleanup;
1952 }
1953
1954 endpt->opts.unixsock->mode = mode;
1955 endpt->opts.unixsock->uid = uid;
1956 endpt->opts.unixsock->gid = gid;
1957
1958cleanup:
1959 /* ENDPT UNLOCK */
1960 pthread_rwlock_unlock(&server_opts.endpt_lock);
1961
1962 return ret;
1963}
1964
1965API int
Michal Vasko59050372016-11-22 14:33:55 +01001966nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001967{
1968 uint32_t i;
1969 int ret = -1;
1970
Michal Vaskoade892d2017-02-22 13:40:35 +01001971 /* BIND LOCK */
1972 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001973
Michal Vaskoade892d2017-02-22 13:40:35 +01001974 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001975 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001976
Michal Vasko59050372016-11-22 14:33:55 +01001977 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001978 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001979 for (i = 0; i < server_opts.endpt_count; ++i) {
1980 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001981 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001982#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001983 case NC_TI_LIBSSH:
1984 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1985 free(server_opts.endpts[i].opts.ssh);
1986 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001987#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001988#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001989 case NC_TI_OPENSSL:
1990 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1991 free(server_opts.endpts[i].opts.tls);
1992 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001993#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001994 case NC_TI_UNIX:
1995 free(server_opts.endpts[i].opts.unixsock);
1996 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001997 default:
1998 ERRINT;
1999 /* won't get here ...*/
2000 break;
2001 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002002 ret = 0;
2003 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002004 free(server_opts.endpts);
2005 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002006
2007 /* remove all binds */
2008 for (i = 0; i < server_opts.endpt_count; ++i) {
2009 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2010 if (server_opts.binds[i].sock > -1) {
2011 close(server_opts.binds[i].sock);
2012 }
2013 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002014 free(server_opts.binds);
2015 server_opts.binds = NULL;
2016
Michal Vasko3031aae2016-01-27 16:07:18 +01002017 server_opts.endpt_count = 0;
2018
Michal Vasko1a38c862016-01-15 15:50:07 +01002019 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002020 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002021 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002022 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002023 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01002024 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002025 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002026#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002027 case NC_TI_LIBSSH:
2028 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2029 free(server_opts.endpts[i].opts.ssh);
2030 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002031#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002032#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002033 case NC_TI_OPENSSL:
2034 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2035 free(server_opts.endpts[i].opts.tls);
2036 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002037#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002038 case NC_TI_UNIX:
2039 free(server_opts.endpts[i].opts.unixsock);
2040 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002041 default:
2042 ERRINT;
2043 break;
2044 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002045
Michal Vaskoe2713da2016-08-22 16:06:40 +02002046 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02002047 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2048 if (server_opts.binds[i].sock > -1) {
2049 close(server_opts.binds[i].sock);
2050 }
2051
2052 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002053 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002054 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002055 free(server_opts.binds);
2056 server_opts.binds = NULL;
2057 free(server_opts.endpts);
2058 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002059 } else if (i < server_opts.endpt_count) {
2060 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2061 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002062 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002063
2064 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002065 if (name) {
2066 break;
2067 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002068 }
2069 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002070 }
2071
Michal Vaskoade892d2017-02-22 13:40:35 +01002072 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002073 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002074
Michal Vaskoade892d2017-02-22 13:40:35 +01002075 /* BIND UNLOCK */
2076 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002077
2078 return ret;
2079}
2080
Michal Vasko71090fc2016-05-24 16:37:28 +02002081API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002082nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002083{
Michal Vasko71090fc2016-05-24 16:37:28 +02002084 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002085 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002086 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002087 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002088 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002089
Michal Vasko45e53ae2016-04-07 11:46:03 +02002090 if (!server_opts.ctx) {
2091 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002092 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002093 } else if (!session) {
2094 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002095 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002096 }
2097
Michal Vaskoade892d2017-02-22 13:40:35 +01002098 /* BIND LOCK */
2099 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002100
2101 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002102 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002103 /* BIND UNLOCK */
2104 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002105 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002106 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002107
Michal Vaskoe2713da2016-08-22 16:06:40 +02002108 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002109 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002110 /* BIND UNLOCK */
2111 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002112 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002113 if (!ret) {
2114 return NC_MSG_WOULDBLOCK;
2115 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002116 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002117 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002118
2119 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2120 /* ENDPT READ LOCK */
2121 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2122
2123 /* BIND UNLOCK */
2124 pthread_mutex_unlock(&server_opts.bind_lock);
2125
Michal Vaskob48aa812016-01-18 14:13:09 +01002126 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002127
Michal Vasko131120a2018-05-29 15:44:02 +02002128 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002129 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002130 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002131 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002132 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002133 msgtype = NC_MSG_ERROR;
2134 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002135 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002136 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002137 (*session)->ctx = server_opts.ctx;
2138 (*session)->flags = NC_SESSION_SHAREDCTX;
2139 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
2140 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002141
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002142 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002143#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002144 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2145 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002146 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002147 if (ret < 0) {
2148 msgtype = NC_MSG_ERROR;
2149 goto cleanup;
2150 } else if (!ret) {
2151 msgtype = NC_MSG_WOULDBLOCK;
2152 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002153 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002154 } else
2155#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002156#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002157 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2158 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002159 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002160 if (ret < 0) {
2161 msgtype = NC_MSG_ERROR;
2162 goto cleanup;
2163 } else if (!ret) {
2164 msgtype = NC_MSG_WOULDBLOCK;
2165 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002166 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002167 } else
2168#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002169 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2170 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2171 ret = nc_accept_unix(*session, sock);
2172 if (ret < 0) {
2173 msgtype = NC_MSG_ERROR;
2174 goto cleanup;
2175 }
2176 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002177 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002178 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002179 msgtype = NC_MSG_ERROR;
2180 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002181 }
2182
Michal Vasko2cc4c682016-03-01 09:16:48 +01002183 (*session)->data = NULL;
2184
Michal Vaskoade892d2017-02-22 13:40:35 +01002185 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002186 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002187
Michal Vaskob48aa812016-01-18 14:13:09 +01002188 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002189 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002190
Michal Vasko9e036d52016-01-08 10:49:26 +01002191 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002192 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002193 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002194 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002195 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002196 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002197 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002198
2199 nc_gettimespec_mono(&ts_cur);
2200 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2201 nc_gettimespec_real(&ts_cur);
2202 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002203 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002204
Michal Vasko71090fc2016-05-24 16:37:28 +02002205 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002206
Michal Vasko71090fc2016-05-24 16:37:28 +02002207cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002208 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002209 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002210
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002211 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002212 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002213 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002214}
2215
Michal Vasko2e6defd2016-10-07 15:48:15 +02002216API int
2217nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
2218{
2219 uint16_t i;
2220
2221 if (!name) {
2222 ERRARG("name");
2223 return -1;
2224 } else if (!ti) {
2225 ERRARG("ti");
2226 return -1;
2227 }
2228
2229 /* WRITE LOCK */
2230 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2231
2232 /* check name uniqueness */
2233 for (i = 0; i < server_opts.ch_client_count; ++i) {
2234 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2235 ERR("Call Home client \"%s\" already exists.", name);
2236 /* WRITE UNLOCK */
2237 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2238 return -1;
2239 }
2240 }
2241
2242 ++server_opts.ch_client_count;
2243 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2244 if (!server_opts.ch_clients) {
2245 ERRMEM;
2246 /* WRITE UNLOCK */
2247 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2248 return -1;
2249 }
2250 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko69a3ff62018-11-09 09:31:48 +01002251 server_opts.ch_clients[server_opts.ch_client_count - 1].id = ATOMIC_INC(&server_opts.new_client_id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002252 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002253 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
2254 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002255
2256 switch (ti) {
2257#ifdef NC_ENABLED_SSH
2258 case NC_TI_LIBSSH:
2259 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2260 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2261 ERRMEM;
2262 /* WRITE UNLOCK */
2263 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2264 return -1;
2265 }
2266 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2267 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2268 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2269 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2270 break;
2271#endif
2272#ifdef NC_ENABLED_TLS
2273 case NC_TI_OPENSSL:
2274 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2275 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2276 ERRMEM;
2277 /* WRITE UNLOCK */
2278 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2279 return -1;
2280 }
2281 break;
2282#endif
2283 default:
2284 ERRINT;
2285 /* WRITE UNLOCK */
2286 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2287 return -1;
2288 }
2289
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002290 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2291
Michal Vasko2e6defd2016-10-07 15:48:15 +02002292 /* set CH default options */
2293 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2294 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2295
2296 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2297
2298 /* WRITE UNLOCK */
2299 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2300
2301 return 0;
2302}
2303
2304API int
Michal Vasko59050372016-11-22 14:33:55 +01002305nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002306{
2307 uint16_t i, j;
2308 int ret = -1;
2309
2310 /* WRITE LOCK */
2311 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2312
Michal Vasko59050372016-11-22 14:33:55 +01002313 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002314 /* remove all CH clients */
2315 for (i = 0; i < server_opts.ch_client_count; ++i) {
2316 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2317
2318 /* remove all endpoints */
2319 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2320 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2321 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2322 }
2323 free(server_opts.ch_clients[i].ch_endpts);
2324
2325 switch (server_opts.ch_clients[i].ti) {
2326#ifdef NC_ENABLED_SSH
2327 case NC_TI_LIBSSH:
2328 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2329 free(server_opts.ch_clients[i].opts.ssh);
2330 break;
2331#endif
2332#ifdef NC_ENABLED_TLS
2333 case NC_TI_OPENSSL:
2334 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2335 free(server_opts.ch_clients[i].opts.tls);
2336 break;
2337#endif
2338 default:
2339 ERRINT;
2340 /* won't get here ...*/
2341 break;
2342 }
2343
2344 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2345
2346 ret = 0;
2347 }
2348 free(server_opts.ch_clients);
2349 server_opts.ch_clients = NULL;
2350
2351 server_opts.ch_client_count = 0;
2352
2353 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002354 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002355 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002356 if ((name && !strcmp(server_opts.ch_clients[i].name, name)) || (!name && (server_opts.ch_clients[i].ti == ti))) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002357 /* remove endpt */
2358 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2359
2360 switch (server_opts.ch_clients[i].ti) {
2361#ifdef NC_ENABLED_SSH
2362 case NC_TI_LIBSSH:
2363 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2364 free(server_opts.ch_clients[i].opts.ssh);
2365 break;
2366#endif
2367#ifdef NC_ENABLED_TLS
2368 case NC_TI_OPENSSL:
2369 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2370 free(server_opts.ch_clients[i].opts.tls);
2371 break;
2372#endif
2373 default:
2374 ERRINT;
2375 break;
2376 }
2377
2378 /* remove all endpoints */
2379 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2380 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2381 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2382 }
2383 free(server_opts.ch_clients[i].ch_endpts);
2384
2385 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2386
2387 /* move last client and endpoint(s) to the empty space */
2388 --server_opts.ch_client_count;
2389 if (i < server_opts.ch_client_count) {
2390 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2391 sizeof *server_opts.ch_clients);
2392 } else if (!server_opts.ch_client_count) {
2393 free(server_opts.ch_clients);
2394 server_opts.ch_clients = NULL;
2395 }
2396
2397 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002398 if (name) {
2399 break;
2400 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002401 }
2402 }
2403 }
2404
2405 /* WRITE UNLOCK */
2406 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2407
2408 return ret;
2409}
2410
2411API int
2412nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2413{
2414 uint16_t i;
2415 struct nc_ch_client *client;
2416
2417 if (!client_name) {
2418 ERRARG("client_name");
2419 return -1;
2420 } else if (!endpt_name) {
2421 ERRARG("endpt_name");
2422 return -1;
2423 }
2424
2425 /* LOCK */
2426 client = nc_server_ch_client_lock(client_name, 0, NULL);
2427 if (!client) {
2428 return -1;
2429 }
2430
2431 for (i = 0; i < client->ch_endpt_count; ++i) {
2432 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2433 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2434 /* UNLOCK */
2435 nc_server_ch_client_unlock(client);
2436 return -1;
2437 }
2438 }
2439
2440 ++client->ch_endpt_count;
2441 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2442 if (!client->ch_endpts) {
2443 ERRMEM;
2444 /* UNLOCK */
2445 nc_server_ch_client_unlock(client);
2446 return -1;
2447 }
2448
2449 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2450 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2451 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
Frank Rimpler9f838b02018-07-25 06:44:03 +00002452 client->ch_endpts[client->ch_endpt_count - 1].sock_pending = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002453
2454 /* UNLOCK */
2455 nc_server_ch_client_unlock(client);
2456
2457 return 0;
2458}
2459
2460API int
2461nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2462{
2463 uint16_t i;
2464 int ret = -1;
2465 struct nc_ch_client *client;
2466
2467 if (!client_name) {
2468 ERRARG("client_name");
2469 return -1;
2470 }
2471
2472 /* LOCK */
2473 client = nc_server_ch_client_lock(client_name, 0, NULL);
2474 if (!client) {
2475 return -1;
2476 }
2477
2478 if (!endpt_name) {
2479 /* remove all endpoints */
2480 for (i = 0; i < client->ch_endpt_count; ++i) {
2481 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2482 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Frank Rimpler9f838b02018-07-25 06:44:03 +00002483 if (client->ch_endpts[i].sock_pending != -1) {
2484 close(client->ch_endpts[i].sock_pending);
2485 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002486 }
2487 free(client->ch_endpts);
2488 client->ch_endpts = NULL;
2489 client->ch_endpt_count = 0;
2490
2491 ret = 0;
2492 } else {
2493 for (i = 0; i < client->ch_endpt_count; ++i) {
2494 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2495 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2496 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002497
Michal Vasko4f921012016-10-20 14:07:45 +02002498 /* move last endpoint to the empty space */
2499 --client->ch_endpt_count;
2500 if (i < client->ch_endpt_count) {
2501 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2502 } else if (!server_opts.ch_client_count) {
2503 free(server_opts.ch_clients);
2504 server_opts.ch_clients = NULL;
2505 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002506
Michal Vasko4f921012016-10-20 14:07:45 +02002507 ret = 0;
2508 break;
2509 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002510 }
2511 }
2512
2513 /* UNLOCK */
2514 nc_server_ch_client_unlock(client);
2515
2516 return ret;
2517}
2518
2519API int
2520nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2521{
2522 uint16_t i;
2523 int ret = -1;
2524 struct nc_ch_client *client;
2525
2526 if (!client_name) {
2527 ERRARG("client_name");
2528 return -1;
2529 } else if (!endpt_name) {
2530 ERRARG("endpt_name");
2531 return -1;
2532 } else if (!address) {
2533 ERRARG("address");
2534 return -1;
2535 }
2536
2537 /* LOCK */
2538 client = nc_server_ch_client_lock(client_name, 0, NULL);
2539 if (!client) {
2540 return -1;
2541 }
2542
2543 for (i = 0; i < client->ch_endpt_count; ++i) {
2544 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2545 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2546 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2547
2548 ret = 0;
2549 break;
2550 }
2551 }
2552
2553 /* UNLOCK */
2554 nc_server_ch_client_unlock(client);
2555
2556 if (ret == -1) {
2557 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2558 }
2559
2560 return ret;
2561}
2562
2563API int
2564nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2565{
2566 uint16_t i;
2567 int ret = -1;
2568 struct nc_ch_client *client;
2569
2570 if (!client_name) {
2571 ERRARG("client_name");
2572 return -1;
2573 } else if (!endpt_name) {
2574 ERRARG("endpt_name");
2575 return -1;
2576 } else if (!port) {
2577 ERRARG("port");
2578 return -1;
2579 }
2580
2581 /* LOCK */
2582 client = nc_server_ch_client_lock(client_name, 0, NULL);
2583 if (!client) {
2584 return -1;
2585 }
2586
2587 for (i = 0; i < client->ch_endpt_count; ++i) {
2588 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2589 client->ch_endpts[i].port = port;
2590
2591 ret = 0;
2592 break;
2593 }
2594 }
2595
2596 /* UNLOCK */
2597 nc_server_ch_client_unlock(client);
2598
2599 if (ret == -1) {
2600 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2601 }
2602
2603 return ret;
2604}
2605
2606API int
2607nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2608{
2609 struct nc_ch_client *client;
2610
2611 if (!client_name) {
2612 ERRARG("client_name");
2613 return -1;
2614 } else if (!conn_type) {
2615 ERRARG("conn_type");
2616 return -1;
2617 }
2618
2619 /* LOCK */
2620 client = nc_server_ch_client_lock(client_name, 0, NULL);
2621 if (!client) {
2622 return -1;
2623 }
2624
2625 if (client->conn_type != conn_type) {
2626 client->conn_type = conn_type;
2627
2628 /* set default options */
2629 switch (conn_type) {
2630 case NC_CH_PERSIST:
2631 client->conn.persist.idle_timeout = 86400;
2632 client->conn.persist.ka_max_wait = 30;
2633 client->conn.persist.ka_max_attempts = 3;
2634 break;
2635 case NC_CH_PERIOD:
2636 client->conn.period.idle_timeout = 300;
2637 client->conn.period.reconnect_timeout = 60;
2638 break;
2639 default:
2640 ERRINT;
2641 break;
2642 }
2643 }
2644
2645 /* UNLOCK */
2646 nc_server_ch_client_unlock(client);
2647
2648 return 0;
2649}
2650
2651API int
2652nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2653{
2654 struct nc_ch_client *client;
2655
2656 if (!client_name) {
2657 ERRARG("client_name");
2658 return -1;
2659 }
2660
2661 /* LOCK */
2662 client = nc_server_ch_client_lock(client_name, 0, NULL);
2663 if (!client) {
2664 return -1;
2665 }
2666
2667 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002668 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002669 /* UNLOCK */
2670 nc_server_ch_client_unlock(client);
2671 return -1;
2672 }
2673
2674 client->conn.persist.idle_timeout = idle_timeout;
2675
2676 /* UNLOCK */
2677 nc_server_ch_client_unlock(client);
2678
2679 return 0;
2680}
2681
2682API int
2683nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2684{
2685 struct nc_ch_client *client;
2686
2687 if (!client_name) {
2688 ERRARG("client_name");
2689 return -1;
2690 } else if (!max_wait) {
2691 ERRARG("max_wait");
2692 return -1;
2693 }
2694
2695 /* LOCK */
2696 client = nc_server_ch_client_lock(client_name, 0, NULL);
2697 if (!client) {
2698 return -1;
2699 }
2700
2701 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002702 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002703 /* UNLOCK */
2704 nc_server_ch_client_unlock(client);
2705 return -1;
2706 }
2707
2708 client->conn.persist.ka_max_wait = max_wait;
2709
2710 /* UNLOCK */
2711 nc_server_ch_client_unlock(client);
2712
2713 return 0;
2714}
2715
2716API int
2717nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2718{
2719 struct nc_ch_client *client;
2720
2721 if (!client_name) {
2722 ERRARG("client_name");
2723 return -1;
2724 }
2725
2726 /* LOCK */
2727 client = nc_server_ch_client_lock(client_name, 0, NULL);
2728 if (!client) {
2729 return -1;
2730 }
2731
2732 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002733 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002734 /* UNLOCK */
2735 nc_server_ch_client_unlock(client);
2736 return -1;
2737 }
2738
2739 client->conn.persist.ka_max_attempts = max_attempts;
2740
2741 /* UNLOCK */
2742 nc_server_ch_client_unlock(client);
2743
2744 return 0;
2745}
2746
2747API int
2748nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2749{
2750 struct nc_ch_client *client;
2751
2752 if (!client_name) {
2753 ERRARG("client_name");
2754 return -1;
2755 }
2756
2757 /* LOCK */
2758 client = nc_server_ch_client_lock(client_name, 0, NULL);
2759 if (!client) {
2760 return -1;
2761 }
2762
2763 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002764 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002765 /* UNLOCK */
2766 nc_server_ch_client_unlock(client);
2767 return -1;
2768 }
2769
2770 client->conn.period.idle_timeout = idle_timeout;
2771
2772 /* UNLOCK */
2773 nc_server_ch_client_unlock(client);
2774
2775 return 0;
2776}
2777
2778API int
2779nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2780{
2781 struct nc_ch_client *client;
2782
2783 if (!client_name) {
2784 ERRARG("client_name");
2785 return -1;
2786 } else if (!reconnect_timeout) {
2787 ERRARG("reconnect_timeout");
2788 return -1;
2789 }
2790
2791 /* LOCK */
2792 client = nc_server_ch_client_lock(client_name, 0, NULL);
2793 if (!client) {
2794 return -1;
2795 }
2796
2797 if (client->conn_type != NC_CH_PERIOD) {
2798 ERR("Call Home client \"%s\" is not of periodic connection type.");
2799 /* UNLOCK */
2800 nc_server_ch_client_unlock(client);
2801 return -1;
2802 }
2803
2804 client->conn.period.reconnect_timeout = reconnect_timeout;
2805
2806 /* UNLOCK */
2807 nc_server_ch_client_unlock(client);
2808
2809 return 0;
2810}
2811
2812API int
2813nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2814{
2815 struct nc_ch_client *client;
2816
2817 if (!client_name) {
2818 ERRARG("client_name");
2819 return -1;
2820 }
2821
2822 /* LOCK */
2823 client = nc_server_ch_client_lock(client_name, 0, NULL);
2824 if (!client) {
2825 return -1;
2826 }
2827
2828 client->start_with = start_with;
2829
2830 /* UNLOCK */
2831 nc_server_ch_client_unlock(client);
2832
2833 return 0;
2834}
2835
2836API int
2837nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2838{
2839 struct nc_ch_client *client;
2840
2841 if (!client_name) {
2842 ERRARG("client_name");
2843 return -1;
2844 } else if (!max_attempts) {
2845 ERRARG("max_attempts");
2846 return -1;
2847 }
2848
2849 /* LOCK */
2850 client = nc_server_ch_client_lock(client_name, 0, NULL);
2851 if (!client) {
2852 return -1;
2853 }
2854
2855 client->max_attempts = max_attempts;
2856
2857 /* UNLOCK */
2858 nc_server_ch_client_unlock(client);
2859
2860 return 0;
2861}
2862
2863/* client lock is expected to be held */
2864static NC_MSG_TYPE
2865nc_connect_ch_client_endpt(struct nc_ch_client *client, struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01002866{
Michal Vasko71090fc2016-05-24 16:37:28 +02002867 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002868 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002869 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01002870 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01002871
Michal Vasko66032bc2019-01-22 15:03:12 +01002872 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002873 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002874 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002875 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00002876 /* no need to store the socket as pending any longer */
2877 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01002878
Michal Vasko131120a2018-05-29 15:44:02 +02002879 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002880 if (!(*session)) {
2881 ERRMEM;
2882 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01002883 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002884 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002885 }
2886 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01002887 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002888 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko66032bc2019-01-22 15:03:12 +01002889 (*session)->host = lydict_insert_zc(server_opts.ctx, ip_host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002890 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002891
Michal Vaskob05053d2016-01-22 16:12:06 +01002892 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002893#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002894 if (client->ti == NC_TI_LIBSSH) {
2895 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002896 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002897 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002898
Michal Vasko71090fc2016-05-24 16:37:28 +02002899 if (ret < 0) {
2900 msgtype = NC_MSG_ERROR;
2901 goto fail;
2902 } else if (!ret) {
2903 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002904 goto fail;
2905 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002906 } else
2907#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002908#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002909 if (client->ti == NC_TI_OPENSSL) {
2910 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002911 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002912 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002913
Michal Vasko71090fc2016-05-24 16:37:28 +02002914 if (ret < 0) {
2915 msgtype = NC_MSG_ERROR;
2916 goto fail;
2917 } else if (!ret) {
2918 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002919 goto fail;
2920 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002921 } else
2922#endif
2923 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002924 ERRINT;
2925 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002926 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002927 goto fail;
2928 }
2929
2930 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002931 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01002932
2933 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002934 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002935 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002936 goto fail;
2937 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002938
2939 nc_gettimespec_mono(&ts_cur);
2940 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2941 nc_gettimespec_real(&ts_cur);
2942 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01002943 (*session)->status = NC_STATUS_RUNNING;
2944
Michal Vasko71090fc2016-05-24 16:37:28 +02002945 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002946
2947fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002948 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002949 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002950 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002951}
2952
Michal Vasko2e6defd2016-10-07 15:48:15 +02002953struct nc_ch_client_thread_arg {
2954 char *client_name;
2955 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2956};
2957
2958static struct nc_ch_client *
2959nc_server_ch_client_with_endpt_lock(const char *name)
2960{
2961 struct nc_ch_client *client;
2962
2963 while (1) {
2964 /* LOCK */
2965 client = nc_server_ch_client_lock(name, 0, NULL);
2966 if (!client) {
2967 return NULL;
2968 }
2969 if (client->ch_endpt_count) {
2970 return client;
2971 }
2972 /* no endpoints defined yet */
2973
2974 /* UNLOCK */
2975 nc_server_ch_client_unlock(client);
2976
2977 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2978 }
2979
2980 return NULL;
2981}
2982
2983static int
2984nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2985{
Michal Vasko3f05a092018-03-13 10:39:49 +01002986 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002987 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002988 struct timespec ts;
2989 struct nc_ch_client *client;
2990
2991 /* session created, initialize condition */
Michal Vasko27377422018-03-15 08:59:35 +01002992 session->opts.server.ch_lock = calloc(1, sizeof *session->opts.server.ch_lock);
2993 session->opts.server.ch_cond = calloc(1, sizeof *session->opts.server.ch_cond);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002994 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2995 ERRMEM;
2996 nc_session_free(session, NULL);
2997 return -1;
2998 }
2999 pthread_mutex_init(session->opts.server.ch_lock, NULL);
3000 pthread_cond_init(session->opts.server.ch_cond, NULL);
3001
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003002 session->flags |= NC_SESSION_CALLHOME;
3003
Michal Vasko2e6defd2016-10-07 15:48:15 +02003004 /* CH LOCK */
3005 pthread_mutex_lock(session->opts.server.ch_lock);
3006
3007 /* give the session to the user */
3008 data->session_clb(data->client_name, session);
3009
3010 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003011 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003012 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003013
Michal Vasko3f05a092018-03-13 10:39:49 +01003014 r = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
3015 if (!r) {
3016 /* we were woken up, something probably happened */
3017 if (session->status != NC_STATUS_RUNNING) {
3018 break;
3019 }
3020 } else if (r != ETIMEDOUT) {
3021 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3022 ret = -1;
3023 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003024 }
3025
Michal Vasko2e6defd2016-10-07 15:48:15 +02003026 /* check whether the client was not removed */
3027 /* LOCK */
3028 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
3029 if (!client) {
3030 /* client was removed, finish thread */
3031 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
3032 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003033 ret = 1;
3034 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003035 }
3036
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003037 if (client->conn_type == NC_CH_PERSIST) {
3038 /* TODO keep-alives */
3039 idle_timeout = client->conn.persist.idle_timeout;
3040 } else {
3041 idle_timeout = client->conn.period.idle_timeout;
3042 }
3043
Michal Vasko9fb42272017-10-05 13:50:05 +02003044 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003045 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 +02003046 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3047 session->status = NC_STATUS_INVALID;
3048 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3049 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003050
3051 /* UNLOCK */
3052 nc_server_ch_client_unlock(client);
3053
3054 } while (session->status == NC_STATUS_RUNNING);
3055
Michal Vasko27377422018-03-15 08:59:35 +01003056 /* CH UNLOCK */
3057 pthread_mutex_unlock(session->opts.server.ch_lock);
3058
Michal Vasko3f05a092018-03-13 10:39:49 +01003059 if (session->status == NC_STATUS_CLOSING) {
3060 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
3061 session->flags &= ~NC_SESSION_CALLHOME;
3062 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003063
Michal Vasko3f05a092018-03-13 10:39:49 +01003064 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003065}
3066
3067static void *
3068nc_ch_client_thread(void *arg)
3069{
3070 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3071 NC_MSG_TYPE msgtype;
3072 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003073 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003074 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003075 struct nc_ch_endpt *cur_endpt;
3076 struct nc_session *session;
3077 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003078 uint32_t client_id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003079
3080 /* LOCK */
3081 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3082 if (!client) {
3083 goto cleanup;
3084 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003085 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003086
3087 cur_endpt = &client->ch_endpts[0];
3088 cur_endpt_name = strdup(cur_endpt->name);
3089
Michal Vasko29af44b2016-10-13 10:59:55 +02003090 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003091 while (1) {
3092 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
3093
3094 if (msgtype == NC_MSG_HELLO) {
3095 /* UNLOCK */
3096 nc_server_ch_client_unlock(client);
3097
Michal Vasko29af44b2016-10-13 10:59:55 +02003098 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003099 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3100 goto cleanup;
3101 }
Michal Vasko29af44b2016-10-13 10:59:55 +02003102 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003103
3104 /* LOCK */
3105 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3106 if (!client) {
3107 goto cleanup;
3108 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003109 if (client->id != client_id) {
3110 nc_server_ch_client_unlock(client);
3111 goto cleanup;
3112 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003113
3114 /* session changed status -> it was disconnected for whatever reason,
3115 * persistent connection immediately tries to reconnect, periodic waits some first */
3116 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003117 /* UNLOCK */
3118 nc_server_ch_client_unlock(client);
3119
3120 /* TODO wake up sometimes to check for new notifications */
3121 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
3122
3123 /* LOCK */
3124 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3125 if (!client) {
3126 goto cleanup;
3127 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003128 if (client->id != client_id) {
3129 nc_server_ch_client_unlock(client);
3130 goto cleanup;
3131 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003132 }
3133
3134 /* set next endpoint to try */
3135 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003136 next_endpt_index = 0;
Michal Vasko2a225342018-09-05 08:38:34 +02003137 } else {
Peter Feiged05f2252018-09-03 08:09:47 +00003138 /* we keep the current one but due to unlock/lock we have to find it again */
3139 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3140 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3141 break;
3142 }
3143 }
3144 if (next_endpt_index >= client->ch_endpt_count) {
3145 /* endpoint was removed, start with the first one */
3146 next_endpt_index = 0;
3147 }
3148 }
3149
Michal Vasko2e6defd2016-10-07 15:48:15 +02003150 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003151 /* UNLOCK */
3152 nc_server_ch_client_unlock(client);
3153
Michal Vasko2e6defd2016-10-07 15:48:15 +02003154 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003155 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3156
Michal Vasko6bb116b2016-10-26 13:53:46 +02003157 /* LOCK */
3158 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3159 if (!client) {
3160 goto cleanup;
3161 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003162 if (client->id != client_id) {
3163 nc_server_ch_client_unlock(client);
3164 goto cleanup;
3165 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003166
Michal Vasko2e6defd2016-10-07 15:48:15 +02003167 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003168
3169 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003170 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3171 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003172 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003173 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003174 }
3175
Peter Feiged05f2252018-09-03 08:09:47 +00003176 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003177 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003178 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003179 cur_attempts = 0;
3180 } else if (cur_attempts == client->max_attempts) {
3181 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003182 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003183 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003184 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003185 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003186 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003187 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003188 }
3189
3190 cur_attempts = 0;
3191 } /* else we keep the current one */
3192 }
Peter Feiged05f2252018-09-03 08:09:47 +00003193
3194 cur_endpt = &client->ch_endpts[next_endpt_index];
3195 free(cur_endpt_name);
3196 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003197 }
3198
3199cleanup:
3200 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003201 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003202 free(data->client_name);
3203 free(data);
3204 return NULL;
3205}
3206
3207API int
3208nc_connect_ch_client_dispatch(const char *client_name,
Michal Vasko3f05a092018-03-13 10:39:49 +01003209 void (*session_clb)(const char *client_name, struct nc_session *new_session))
3210{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003211 int ret;
3212 pthread_t tid;
3213 struct nc_ch_client_thread_arg *arg;
3214
3215 if (!client_name) {
3216 ERRARG("client_name");
3217 return -1;
3218 } else if (!session_clb) {
3219 ERRARG("session_clb");
3220 return -1;
3221 }
3222
3223 arg = malloc(sizeof *arg);
3224 if (!arg) {
3225 ERRMEM;
3226 return -1;
3227 }
3228 arg->client_name = strdup(client_name);
3229 if (!arg->client_name) {
3230 ERRMEM;
3231 free(arg);
3232 return -1;
3233 }
3234 arg->session_clb = session_clb;
3235
3236 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3237 if (ret) {
3238 ERR("Creating a new thread failed (%s).", strerror(ret));
3239 free(arg->client_name);
3240 free(arg);
3241 return -1;
3242 }
3243 /* the thread now manages arg */
3244
3245 pthread_detach(tid);
3246
3247 return 0;
3248}
3249
Radek Krejci53691be2016-02-22 13:58:37 +01003250#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003251
Michal Vaskoe8e07702017-03-15 10:19:30 +01003252API int
3253nc_server_endpt_count(void)
3254{
3255 return server_opts.endpt_count;
3256}
3257
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003258API time_t
3259nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003260{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003261 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003262 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003263 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003264 }
3265
Michal Vasko2e6defd2016-10-07 15:48:15 +02003266 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003267}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003268
3269API void
3270nc_session_set_notif_status(struct nc_session *session, int notif_status)
3271{
3272 if (!session || (session->side != NC_SERVER)) {
3273 ERRARG("session");
3274 return;
3275 }
3276
3277 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3278}
3279
3280API int
3281nc_session_get_notif_status(const struct nc_session *session)
3282{
3283 if (!session || (session->side != NC_SERVER)) {
3284 ERRARG("session");
3285 return 0;
3286 }
3287
3288 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003289}
Michal Vasko8f430592019-02-26 08:32:54 +01003290
3291API int
3292nc_session_is_callhome(const struct nc_session *session)
3293{
3294 if (!session || (session->side != NC_SERVER)) {
3295 ERRARG("session");
3296 return 0;
3297 }
3298
3299 if (session->flags & NC_SESSION_CALLHOME) {
3300 return 1;
3301 }
3302
3303 return 0;
3304}