blob: b884bc9a9b8e8132d9a9d8f0d941a0aa59870720 [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) {
563 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
564 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
565 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
566 }
567 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
568 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
569 }
570 } else {
571 ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
572 }
573 pthread_rwlockattr_destroy(&attr);
574 } else {
575 ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
576 }
Michal Vasko086311b2016-01-08 09:53:11 +0100577 return 0;
578}
579
Michal Vaskob48aa812016-01-18 14:13:09 +0100580API void
581nc_server_destroy(void)
582{
Radek Krejci658782b2016-12-04 22:04:55 +0100583 unsigned int i;
584
585 for (i = 0; i < server_opts.capabilities_count; i++) {
586 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
587 }
588 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200589 server_opts.capabilities = NULL;
590 server_opts.capabilities_count = 0;
591
Radek Krejci53691be2016-02-22 13:58:37 +0100592#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100593 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100594#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100595#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100596 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
597 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
598 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200599 server_opts.passwd_auth_data = NULL;
600 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100601
Michal Vasko17dfda92016-12-01 14:06:16 +0100602 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100603
604 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
605 server_opts.hostkey_data_free(server_opts.hostkey_data);
606 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200607 server_opts.hostkey_data = NULL;
608 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100609#endif
610#ifdef NC_ENABLED_TLS
611 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
612 server_opts.server_cert_data_free(server_opts.server_cert_data);
613 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200614 server_opts.server_cert_data = NULL;
615 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100616 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
617 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
618 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200619 server_opts.trusted_cert_list_data = NULL;
620 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100621#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100622 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100623}
624
Michal Vasko086311b2016-01-08 09:53:11 +0100625API int
626nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
627{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200628 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
629 ERRARG("basic_mode");
630 return -1;
631 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
632 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100633 return -1;
634 }
635
636 server_opts.wd_basic_mode = basic_mode;
637 server_opts.wd_also_supported = also_supported;
638 return 0;
639}
640
Michal Vasko1a38c862016-01-15 15:50:07 +0100641API void
Michal Vasko55f03972016-04-13 08:56:01 +0200642nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
643{
644 if (!basic_mode && !also_supported) {
645 ERRARG("basic_mode and also_supported");
646 return;
647 }
648
649 if (basic_mode) {
650 *basic_mode = server_opts.wd_basic_mode;
651 }
652 if (also_supported) {
653 *also_supported = server_opts.wd_also_supported;
654 }
655}
656
Michal Vasko55f03972016-04-13 08:56:01 +0200657API int
Radek Krejci658782b2016-12-04 22:04:55 +0100658nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200659{
Radek Krejci658782b2016-12-04 22:04:55 +0100660 const char **new;
661
662 if (!value || !value[0]) {
663 ERRARG("value must not be empty");
664 return EXIT_FAILURE;
665 }
666
667 server_opts.capabilities_count++;
668 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
669 if (!new) {
670 ERRMEM;
671 return EXIT_FAILURE;
672 }
673 server_opts.capabilities = new;
674 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
675
676 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200677}
678
Michal Vasko1a38c862016-01-15 15:50:07 +0100679API void
Michal Vasko086311b2016-01-08 09:53:11 +0100680nc_server_set_hello_timeout(uint16_t hello_timeout)
681{
Michal Vasko086311b2016-01-08 09:53:11 +0100682 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100683}
684
Michal Vasko55f03972016-04-13 08:56:01 +0200685API uint16_t
686nc_server_get_hello_timeout(void)
687{
688 return server_opts.hello_timeout;
689}
690
Michal Vasko1a38c862016-01-15 15:50:07 +0100691API void
Michal Vasko086311b2016-01-08 09:53:11 +0100692nc_server_set_idle_timeout(uint16_t idle_timeout)
693{
Michal Vasko086311b2016-01-08 09:53:11 +0100694 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100695}
696
Michal Vasko55f03972016-04-13 08:56:01 +0200697API uint16_t
698nc_server_get_idle_timeout(void)
699{
700 return server_opts.idle_timeout;
701}
702
Michal Vasko71090fc2016-05-24 16:37:28 +0200703API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100704nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100705{
Michal Vasko71090fc2016-05-24 16:37:28 +0200706 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200707 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200708
Michal Vasko45e53ae2016-04-07 11:46:03 +0200709 if (!server_opts.ctx) {
710 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200711 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200712 } else if (fdin < 0) {
713 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200714 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200715 } else if (fdout < 0) {
716 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200717 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200718 } else if (!username) {
719 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200720 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200721 } else if (!session) {
722 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200723 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100724 }
725
726 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200727 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100728 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100729 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200730 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100731 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100732 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100733
Michal Vasko086311b2016-01-08 09:53:11 +0100734 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100735 (*session)->ti_type = NC_TI_FD;
736 (*session)->ti.fd.in = fdin;
737 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100738
739 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100740 (*session)->flags = NC_SESSION_SHAREDCTX;
741 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100742
Michal Vaskob48aa812016-01-18 14:13:09 +0100743 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +0100744 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100745
Michal Vasko086311b2016-01-08 09:53:11 +0100746 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200747 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200748 if (msgtype != NC_MSG_HELLO) {
749 nc_session_free(*session, NULL);
750 *session = NULL;
751 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100752 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200753
754 nc_gettimespec_mono(&ts_cur);
755 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
756 nc_gettimespec_real(&ts_cur);
757 (*session)->opts.server.session_start = ts_cur.tv_sec;
758
Michal Vasko1a38c862016-01-15 15:50:07 +0100759 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100760
Michal Vasko71090fc2016-05-24 16:37:28 +0200761 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100762}
Michal Vasko9e036d52016-01-08 10:49:26 +0100763
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200764static int
765nc_accept_unix(struct nc_session *session, int sock)
766{
767 const struct passwd *pw;
768 struct ucred ucred;
769 char *username;
770 socklen_t len;
771
772 session->ti_type = NC_TI_UNIX;
773
774 len = sizeof(ucred);
775 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
776 ERR("Failed to get credentials from unix socket (%s).",
777 strerror(errno));
778 close(sock);
779 return -1;
780 }
781
782 pw = getpwuid(ucred.uid);
783 if (pw == NULL) {
784 ERR("Failed to find username for uid=%u (%s).\n", ucred.uid,
785 strerror(errno));
786 close(sock);
787 return -1;
788 }
789
790 username = strdup(pw->pw_name);
791 if (username == NULL) {
792 ERRMEM;
793 close(sock);
794 return -1;
795 }
796 session->username = lydict_insert_zc(server_opts.ctx, username);
797
798 session->ti.unixsock.sock = sock;
799
800 return 1;
801}
802
Michal Vaskob30b99c2016-07-26 11:35:43 +0200803static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100804nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
805{
806 uint8_t q_last;
807
808 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
809 ERRINT;
810 return;
811 }
812
813 /* get a unique queue value (by adding 1 to the last added value, if any) */
814 if (ps->queue_len) {
815 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
816 *id = ps->queue[q_last] + 1;
817 } else {
818 *id = 0;
819 }
820
821 /* add the id into the queue */
822 ++ps->queue_len;
823 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
824 ps->queue[q_last] = *id;
825}
826
827static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200828nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
829{
Michal Vasko74c345f2018-02-07 10:37:11 +0100830 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200831
832 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100833 /* get the actual queue idx */
834 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200835
836 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100837 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200838 /* another equal value, simply cannot be */
839 ERRINT;
840 }
Michal Vaskod8340032018-02-12 14:41:00 +0100841 if (found == 2) {
842 /* move the following values */
843 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
844 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100845 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200846 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100847 if (i == 0) {
848 found = 1;
849 } else {
850 /* this is not okay, our id is in the middle of the queue */
851 found = 2;
852 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200853 }
854 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200855 if (!found) {
856 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100857 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200858 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100859
Michal Vasko103fe632018-02-12 16:37:45 +0100860 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100861 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100862 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100863 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
864 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200865}
866
Michal Vaskof04a52a2016-04-07 10:52:10 +0200867int
Michal Vasko26043172016-07-26 14:08:59 +0200868nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200869{
870 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200871 struct timespec ts;
872
Michal Vasko77a6abe2017-10-05 10:02:20 +0200873 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100874 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200875
876 /* LOCK */
877 ret = pthread_mutex_timedlock(&ps->lock, &ts);
878 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200879 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200880 return -1;
881 }
882
Michal Vasko74c345f2018-02-07 10:37:11 +0100883 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100884 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100885 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100886 pthread_mutex_unlock(&ps->lock);
887 return -1;
888 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100889
890 /* add ourselves into the queue */
891 nc_ps_queue_add_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200892
893 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200894 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200895 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100896 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200897
898 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
899 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +0530900 /**
901 * This may happen when another thread releases the lock and broadcasts the condition
902 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
903 * but when actually this thread was ready for condition.
904 */
preetbhansali629dfc42018-12-17 16:04:40 +0530905 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +0530906 break;
907 }
Michal Vasko66032bc2019-01-22 15:03:12 +0100908
Michal Vasko26043172016-07-26 14:08:59 +0200909 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200910 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200911 nc_ps_queue_remove_id(ps, *id);
912 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200913 return -1;
914 }
915 }
916
Michal Vaskobe86fe32016-04-07 10:43:03 +0200917 /* UNLOCK */
918 pthread_mutex_unlock(&ps->lock);
919
920 return 0;
921}
922
Michal Vaskof04a52a2016-04-07 10:52:10 +0200923int
Michal Vasko26043172016-07-26 14:08:59 +0200924nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200925{
926 int ret;
927 struct timespec ts;
928
Michal Vasko77a6abe2017-10-05 10:02:20 +0200929 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100930 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200931
932 /* LOCK */
933 ret = pthread_mutex_timedlock(&ps->lock, &ts);
934 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200935 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200936 ret = -1;
937 }
938
Michal Vaskob30b99c2016-07-26 11:35:43 +0200939 /* we must be the first, it was our turn after all, right? */
940 if (ps->queue[ps->queue_begin] != id) {
941 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200942 /* UNLOCK */
943 if (!ret) {
944 pthread_mutex_unlock(&ps->lock);
945 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200946 return -1;
947 }
948
Michal Vaskobe86fe32016-04-07 10:43:03 +0200949 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200950 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200951
952 /* broadcast to all other threads that the queue moved */
953 pthread_cond_broadcast(&ps->cond);
954
Michal Vaskobe86fe32016-04-07 10:43:03 +0200955 /* UNLOCK */
956 if (!ret) {
957 pthread_mutex_unlock(&ps->lock);
958 }
959
960 return ret;
961}
962
Michal Vasko428087d2016-01-14 16:04:28 +0100963API struct nc_pollsession *
964nc_ps_new(void)
965{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100966 struct nc_pollsession *ps;
967
968 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100969 if (!ps) {
970 ERRMEM;
971 return NULL;
972 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200973 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100974 pthread_mutex_init(&ps->lock, NULL);
975
976 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100977}
978
979API void
980nc_ps_free(struct nc_pollsession *ps)
981{
fanchanghu3d4e7212017-08-09 09:42:30 +0800982 uint16_t i;
983
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100984 if (!ps) {
985 return;
986 }
987
Michal Vaskobe86fe32016-04-07 10:43:03 +0200988 if (ps->queue_len) {
989 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
990 }
991
fanchanghu3d4e7212017-08-09 09:42:30 +0800992 for (i = 0; i < ps->session_count; i++) {
993 free(ps->sessions[i]);
994 }
995
Michal Vasko428087d2016-01-14 16:04:28 +0100996 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100997 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200998 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100999
Michal Vasko428087d2016-01-14 16:04:28 +01001000 free(ps);
1001}
1002
1003API int
1004nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1005{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001006 uint8_t q_id;
1007
Michal Vasko45e53ae2016-04-07 11:46:03 +02001008 if (!ps) {
1009 ERRARG("ps");
1010 return -1;
1011 } else if (!session) {
1012 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001013 return -1;
1014 }
1015
Michal Vasko48a63ed2016-03-01 09:48:21 +01001016 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001017 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001018 return -1;
1019 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001020
Michal Vasko428087d2016-01-14 16:04:28 +01001021 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001022 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001023 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001024 ERRMEM;
1025 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001026 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001027 return -1;
1028 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001029 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1030 if (!ps->sessions[ps->session_count - 1]) {
1031 ERRMEM;
1032 --ps->session_count;
1033 /* UNLOCK */
1034 nc_ps_unlock(ps, q_id, __func__);
1035 return -1;
1036 }
1037 ps->sessions[ps->session_count - 1]->session = session;
1038 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001039
Michal Vasko48a63ed2016-03-01 09:48:21 +01001040 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001041 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001042}
1043
Michal Vasko48a63ed2016-03-01 09:48:21 +01001044static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001045_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001046{
1047 uint16_t i;
1048
Radek Krejcid5f978f2016-03-03 13:14:45 +01001049 if (index >= 0) {
1050 i = (uint16_t)index;
1051 goto remove;
1052 }
Michal Vasko428087d2016-01-14 16:04:28 +01001053 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001054 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001055remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001056 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001057 if (i <= ps->session_count) {
1058 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001059 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001060 }
1061 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001062 free(ps->sessions);
1063 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001064 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001065 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001066 return 0;
1067 }
1068 }
1069
Michal Vaskof0537d82016-01-29 14:42:38 +01001070 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001071}
1072
Michal Vasko48a63ed2016-03-01 09:48:21 +01001073API int
1074nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1075{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001076 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001077 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001078
Michal Vasko45e53ae2016-04-07 11:46:03 +02001079 if (!ps) {
1080 ERRARG("ps");
1081 return -1;
1082 } else if (!session) {
1083 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001084 return -1;
1085 }
1086
1087 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001088 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001089 return -1;
1090 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001091
Radek Krejcid5f978f2016-03-03 13:14:45 +01001092 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001093
1094 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001095 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001096
Michal Vaskobe86fe32016-04-07 10:43:03 +02001097 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001098}
1099
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001100API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001101nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001102{
1103 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001104 struct nc_session *ret = NULL;
1105
1106 if (!ps) {
1107 ERRARG("ps");
1108 return NULL;
1109 }
1110
1111 /* LOCK */
1112 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1113 return NULL;
1114 }
1115
Michal Vasko4871c9d2017-10-09 14:48:39 +02001116 if (idx < ps->session_count) {
1117 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001118 }
1119
1120 /* UNLOCK */
1121 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1122
1123 return ret;
1124}
1125
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001126API uint16_t
1127nc_ps_session_count(struct nc_pollsession *ps)
1128{
1129 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001130 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001131 return 0;
1132 }
1133
Michal Vaskof4462fd2017-02-15 14:29:05 +01001134 return ps->session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001135}
1136
Michal Vasko131120a2018-05-29 15:44:02 +02001137/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001138 * returns: NC_PSPOLL_ERROR,
1139 * NC_PSPOLL_BAD_RPC,
1140 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1141 * NC_PSPOLL_RPC
1142 */
1143static int
Michal Vasko131120a2018-05-29 15:44:02 +02001144nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001145{
1146 struct lyxml_elem *xml = NULL;
1147 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001148 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001149 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001150
Michal Vasko45e53ae2016-04-07 11:46:03 +02001151 if (!session) {
1152 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001153 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001154 } else if (!rpc) {
1155 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001156 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001157 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001158 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001159 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001160 }
1161
Michal Vasko131120a2018-05-29 15:44:02 +02001162 msgtype = nc_read_msg_io(session, io_timeout, &xml, 0);
Michal Vasko428087d2016-01-14 16:04:28 +01001163
1164 switch (msgtype) {
1165 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001166 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001167 if (!*rpc) {
1168 ERRMEM;
1169 goto error;
1170 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001171
Radek Krejcif93c7d42016-04-06 13:41:15 +02001172 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001173 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1174 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001175 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001176 /* parsing RPC failed */
Michal Vaskoc9970242018-02-14 16:03:35 +01001177 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
Michal Vasko131120a2018-05-29 15:44:02 +02001178 ret = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001179 nc_server_reply_free(reply);
1180 if (ret == -1) {
1181 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001182 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001183 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1184 } else {
1185 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001186 }
Michal Vasko428087d2016-01-14 16:04:28 +01001187 (*rpc)->root = xml;
1188 break;
1189 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001190 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001191 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001192 goto error;
1193 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001194 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001195 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001196 goto error;
1197 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001198 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001199 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001200 goto error;
1201 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001202 /* NC_MSG_ERROR,
Michal Vasko131120a2018-05-29 15:44:02 +02001203 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg_io()
Michal Vasko428087d2016-01-14 16:04:28 +01001204 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001205 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001206 break;
1207 }
1208
Michal Vasko71090fc2016-05-24 16:37:28 +02001209 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001210
1211error:
1212 /* cleanup */
1213 lyxml_free(server_opts.ctx, xml);
1214
Michal Vasko71090fc2016-05-24 16:37:28 +02001215 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001216}
1217
fanchanghu966f2de2016-07-21 02:28:57 -04001218API void
1219nc_set_global_rpc_clb(nc_rpc_clb clb)
1220{
1221 global_rpc_clb = clb;
1222}
1223
Radek Krejci93e80222016-10-03 13:34:25 +02001224API NC_MSG_TYPE
1225nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1226{
Michal Vasko131120a2018-05-29 15:44:02 +02001227 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001228
1229 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001230 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001231 ERRARG("session");
1232 return NC_MSG_ERROR;
1233 } else if (!notif || !notif->tree || !notif->eventtime) {
1234 ERRARG("notif");
1235 return NC_MSG_ERROR;
1236 }
1237
Michal Vasko131120a2018-05-29 15:44:02 +02001238 /* we do not need RPC lock for this, IO lock will be acquired properly */
1239 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
1240 if (ret == NC_MSG_ERROR) {
Radek Krejci93e80222016-10-03 13:34:25 +02001241 ERR("Session %u: failed to write notification.", session->id);
Radek Krejci93e80222016-10-03 13:34:25 +02001242 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001243
Michal Vasko131120a2018-05-29 15:44:02 +02001244 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001245}
1246
Michal Vasko131120a2018-05-29 15:44:02 +02001247/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001248 * returns: NC_PSPOLL_ERROR,
1249 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1250 * NC_PSPOLL_REPLY_ERROR,
1251 * 0
1252 */
1253static int
Michal Vasko131120a2018-05-29 15:44:02 +02001254nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001255{
1256 nc_rpc_clb clb;
1257 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001258 struct lys_node *rpc_act = NULL;
1259 struct lyd_node *next, *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001260 int ret = 0;
1261 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001262
Michal Vasko4a827e52016-03-03 10:59:00 +01001263 if (!rpc) {
1264 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001265 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001266 }
1267
Michal Vasko90e8e692016-07-13 12:27:57 +02001268 if (rpc->tree->schema->nodetype == LYS_RPC) {
1269 /* RPC */
1270 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001271 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001272 /* action */
1273 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1274 if (elem->schema->nodetype == LYS_ACTION) {
1275 rpc_act = elem->schema;
1276 break;
1277 }
1278 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001279 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001280 if (!rpc_act) {
1281 ERRINT;
1282 return NC_PSPOLL_ERROR;
1283 }
1284 }
1285
1286 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001287 if (!global_rpc_clb) {
1288 /* no callback, reply with a not-implemented error */
1289 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
1290 } else {
1291 reply = global_rpc_clb(rpc->tree, session);
1292 }
Michal Vasko428087d2016-01-14 16:04:28 +01001293 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001294 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001295 reply = clb(rpc->tree, session);
1296 }
1297
1298 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001299 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001300 }
Michal Vasko131120a2018-05-29 15:44:02 +02001301 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->root, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001302 if (reply->type == NC_RPL_ERROR) {
1303 ret |= NC_PSPOLL_REPLY_ERROR;
1304 }
1305 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001306
Michal Vasko131120a2018-05-29 15:44:02 +02001307 if (r != NC_MSG_REPLY) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001308 ERR("Session %u: failed to write reply.", session->id);
1309 ret |= NC_PSPOLL_ERROR;
1310 }
Michal Vasko428087d2016-01-14 16:04:28 +01001311
1312 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1313 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1314 session->status = NC_STATUS_INVALID;
1315 }
1316
Michal Vasko71090fc2016-05-24 16:37:28 +02001317 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001318}
1319
Michal Vasko131120a2018-05-29 15:44:02 +02001320/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001321 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1322 * NC_PSPOLL_ERROR, (msg filled)
1323 * NC_PSPOLL_TIMEOUT,
1324 * NC_PSPOLL_RPC (some application data available),
1325 * NC_PSPOLL_SSH_CHANNEL,
1326 * NC_PSPOLL_SSH_MSG
1327 */
1328static int
Michal Vasko131120a2018-05-29 15:44:02 +02001329nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001330{
Michal Vasko9a327362017-01-11 11:31:46 +01001331 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001332 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001333#ifdef NC_ENABLED_SSH
1334 struct nc_session *new;
1335#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001336
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001337 /* check timeout first */
1338 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001339 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001340 sprintf(msg, "session idle timeout elapsed");
1341 session->status = NC_STATUS_INVALID;
1342 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1343 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1344 }
1345
Michal Vasko131120a2018-05-29 15:44:02 +02001346 r = nc_session_io_lock(session, io_timeout, __func__);
1347 if (r < 0) {
1348 sprintf(msg, "session IO lock failed to be acquired");
1349 return NC_PSPOLL_ERROR;
1350 } else if (!r) {
1351 return NC_PSPOLL_TIMEOUT;
1352 }
1353
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001354 switch (session->ti_type) {
1355#ifdef NC_ENABLED_SSH
1356 case NC_TI_LIBSSH:
1357 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001358 if (r == SSH_EOF) {
1359 sprintf(msg, "SSH channel unexpected EOF");
1360 session->status = NC_STATUS_INVALID;
1361 session->term_reason = NC_SESSION_TERM_DROPPED;
1362 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1363 } else if (r == SSH_ERROR) {
1364 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001365 session->status = NC_STATUS_INVALID;
1366 session->term_reason = NC_SESSION_TERM_OTHER;
1367 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001368 } else if (!r) {
1369 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1370 /* new SSH message */
1371 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1372 if (session->ti.libssh.next) {
1373 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1374 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1375 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1376 /* new NETCONF SSH channel */
1377 ret = NC_PSPOLL_SSH_CHANNEL;
1378 break;
1379 }
1380 }
1381 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001382 break;
1383 }
1384 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001385
Michal Vasko8dcaa882017-10-19 14:28:42 +02001386 /* just some SSH message */
1387 ret = NC_PSPOLL_SSH_MSG;
1388 } else {
1389 ret = NC_PSPOLL_TIMEOUT;
1390 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001391 } else {
1392 /* we have some application data */
1393 ret = NC_PSPOLL_RPC;
1394 }
1395 break;
1396#endif
1397#ifdef NC_ENABLED_TLS
1398 case NC_TI_OPENSSL:
1399 r = SSL_pending(session->ti.tls);
1400 if (!r) {
1401 /* no data pending in the SSL buffer, poll fd */
1402 pfd.fd = SSL_get_rfd(session->ti.tls);
1403 if (pfd.fd < 0) {
1404 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1405 ret = NC_PSPOLL_ERROR;
1406 break;
1407 }
1408 pfd.events = POLLIN;
1409 pfd.revents = 0;
1410 r = poll(&pfd, 1, 0);
1411
1412 if ((r < 0) && (errno != EINTR)) {
1413 sprintf(msg, "poll failed (%s)", strerror(errno));
1414 session->status = NC_STATUS_INVALID;
1415 ret = NC_PSPOLL_ERROR;
1416 } else if (r > 0) {
1417 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1418 sprintf(msg, "communication socket unexpectedly closed");
1419 session->status = NC_STATUS_INVALID;
1420 session->term_reason = NC_SESSION_TERM_DROPPED;
1421 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1422 } else if (pfd.revents & POLLERR) {
1423 sprintf(msg, "communication socket error");
1424 session->status = NC_STATUS_INVALID;
1425 session->term_reason = NC_SESSION_TERM_OTHER;
1426 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1427 } else {
1428 ret = NC_PSPOLL_RPC;
1429 }
1430 } else {
1431 ret = NC_PSPOLL_TIMEOUT;
1432 }
1433 } else {
1434 ret = NC_PSPOLL_RPC;
1435 }
1436 break;
1437#endif
1438 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001439 case NC_TI_UNIX:
1440 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001441 pfd.events = POLLIN;
1442 pfd.revents = 0;
1443 r = poll(&pfd, 1, 0);
1444
1445 if ((r < 0) && (errno != EINTR)) {
1446 sprintf(msg, "poll failed (%s)", strerror(errno));
1447 session->status = NC_STATUS_INVALID;
1448 ret = NC_PSPOLL_ERROR;
1449 } else if (r > 0) {
1450 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1451 sprintf(msg, "communication socket unexpectedly closed");
1452 session->status = NC_STATUS_INVALID;
1453 session->term_reason = NC_SESSION_TERM_DROPPED;
1454 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1455 } else if (pfd.revents & POLLERR) {
1456 sprintf(msg, "communication socket error");
1457 session->status = NC_STATUS_INVALID;
1458 session->term_reason = NC_SESSION_TERM_OTHER;
1459 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1460 } else {
1461 ret = NC_PSPOLL_RPC;
1462 }
1463 } else {
1464 ret = NC_PSPOLL_TIMEOUT;
1465 }
1466 break;
1467 case NC_TI_NONE:
1468 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1469 ret = NC_PSPOLL_ERROR;
1470 break;
1471 }
1472
Michal Vasko131120a2018-05-29 15:44:02 +02001473 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001474 return ret;
1475}
1476
1477API int
1478nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1479{
1480 int ret, r;
1481 uint8_t q_id;
1482 uint16_t i, j;
1483 char msg[256];
1484 struct timespec ts_timeout, ts_cur;
1485 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001486 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001487 struct nc_server_rpc *rpc = NULL;
1488
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001489 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001490 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001491 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001492 }
1493
Michal Vaskoade892d2017-02-22 13:40:35 +01001494 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001495 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001496 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001497 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001498
Michal Vaskoade892d2017-02-22 13:40:35 +01001499 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001500 nc_ps_unlock(ps, q_id, __func__);
1501 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001502 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001503
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001504 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001505 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001506 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001507 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001508 nc_addtimespec(&ts_timeout, timeout);
1509 }
1510
1511 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001512 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001513 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001514 if (ps->last_event_session == ps->session_count - 1) {
1515 i = j = 0;
1516 } else {
1517 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001518 }
Michal Vasko9a327362017-01-11 11:31:46 +01001519 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001520 cur_ps_session = ps->sessions[i];
1521 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001522
Michal Vasko131120a2018-05-29 15:44:02 +02001523 /* SESSION RPC LOCK */
1524 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001525 if (r == -1) {
1526 ret = NC_PSPOLL_ERROR;
1527 } else if (r == 1) {
1528 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001529 switch (cur_ps_session->state) {
1530 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001531 if (cur_session->status == NC_STATUS_RUNNING) {
1532 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001533 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001534
Michal Vasko131120a2018-05-29 15:44:02 +02001535 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001536 switch (ret) {
1537 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1538 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001539 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001540 break;
1541 case NC_PSPOLL_ERROR:
1542 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001543 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001544 break;
1545 case NC_PSPOLL_TIMEOUT:
1546#ifdef NC_ENABLED_SSH
1547 case NC_PSPOLL_SSH_CHANNEL:
1548 case NC_PSPOLL_SSH_MSG:
1549#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001550 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001551 break;
1552 case NC_PSPOLL_RPC:
1553 /* let's keep the state busy, we are not done with this session */
1554 break;
1555 }
1556 } else {
1557 /* session is not fine, let the caller know */
1558 ret = NC_PSPOLL_SESSION_TERM;
1559 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1560 ret |= NC_PSPOLL_SESSION_ERROR;
1561 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001562 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001563 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001564 break;
1565 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001566 /* it definitely should not be busy because we have the lock */
1567 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001568 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001569 break;
1570 case NC_PS_STATE_INVALID:
1571 /* we got it locked, but it will be freed, let it be */
1572 ret = NC_PSPOLL_TIMEOUT;
1573 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001574 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001575
Michal Vasko131120a2018-05-29 15:44:02 +02001576 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001577 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001578 /* SESSION RPC UNLOCK */
1579 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001580 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001581 } else {
1582 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001583 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001584 }
Michal Vasko428087d2016-01-14 16:04:28 +01001585
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001586 /* something happened */
1587 if (ret != NC_PSPOLL_TIMEOUT) {
1588 break;
1589 }
1590
Michal Vasko9a327362017-01-11 11:31:46 +01001591 if (i == ps->session_count - 1) {
1592 i = 0;
1593 } else {
1594 ++i;
1595 }
1596 } while (i != j);
1597
Michal Vaskoade892d2017-02-22 13:40:35 +01001598 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001599 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001600 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001601 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001602 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001603
1604 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1605 /* final timeout */
1606 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001607 }
Michal Vasko428087d2016-01-14 16:04:28 +01001608 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001609 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001610
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001611 /* do we want to return the session? */
1612 switch (ret) {
1613 case NC_PSPOLL_RPC:
1614 case NC_PSPOLL_SESSION_TERM:
1615 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1616#ifdef NC_ENABLED_SSH
1617 case NC_PSPOLL_SSH_CHANNEL:
1618 case NC_PSPOLL_SSH_MSG:
1619#endif
1620 if (session) {
1621 *session = cur_session;
1622 }
1623 ps->last_event_session = i;
1624 break;
1625 default:
1626 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001627 }
Michal Vasko428087d2016-01-14 16:04:28 +01001628
Michal Vaskoade892d2017-02-22 13:40:35 +01001629 /* PS UNLOCK */
1630 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001631
Michal Vasko131120a2018-05-29 15:44:02 +02001632 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001633 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001634 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001635 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1636 if (cur_session->status != NC_STATUS_RUNNING) {
1637 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001638 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001639 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001640 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001641 }
1642 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001643 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001644
Michal Vasko7f1ee932018-10-11 09:41:42 +02001645 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001646 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001647 if (cur_session->status != NC_STATUS_RUNNING) {
1648 ret |= NC_PSPOLL_SESSION_TERM;
1649 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1650 ret |= NC_PSPOLL_SESSION_ERROR;
1651 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001652 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001653 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001654 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001655 }
Michal Vasko428087d2016-01-14 16:04:28 +01001656 }
Michal Vasko7f1ee932018-10-11 09:41:42 +02001657 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001658
Michal Vasko131120a2018-05-29 15:44:02 +02001659 /* SESSION RPC UNLOCK */
1660 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001661 }
1662
Michal Vasko48a63ed2016-03-01 09:48:21 +01001663 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001664}
1665
Michal Vaskod09eae62016-02-01 10:32:52 +01001666API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001667nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001668{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001669 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001670 uint16_t i;
1671 struct nc_session *session;
1672
Michal Vasko9a25e932016-02-01 10:36:42 +01001673 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001674 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001675 return;
1676 }
1677
Michal Vasko48a63ed2016-03-01 09:48:21 +01001678 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001679 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001680 return;
1681 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001682
Michal Vasko48a63ed2016-03-01 09:48:21 +01001683 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001684 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001685 nc_session_free(ps->sessions[i]->session, data_free);
1686 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001687 }
1688 free(ps->sessions);
1689 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001690 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001691 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001692 } else {
1693 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001694 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1695 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001696 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001697 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001698 continue;
1699 }
1700
1701 ++i;
1702 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001703 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001704
1705 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001706 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001707}
1708
Radek Krejci53691be2016-02-22 13:58:37 +01001709#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001710
Michal Vaskoe2713da2016-08-22 16:06:40 +02001711API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001712nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001713{
Michal Vasko3031aae2016-01-27 16:07:18 +01001714 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001715 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001716
Michal Vasko45e53ae2016-04-07 11:46:03 +02001717 if (!name) {
1718 ERRARG("name");
1719 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001720 }
1721
Michal Vaskoade892d2017-02-22 13:40:35 +01001722 /* BIND LOCK */
1723 pthread_mutex_lock(&server_opts.bind_lock);
1724
1725 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001726 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001727
1728 /* check name uniqueness */
1729 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001730 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001731 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001732 ret = -1;
1733 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001734 }
1735 }
1736
Michal Vasko3031aae2016-01-27 16:07:18 +01001737 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001738 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001739 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001740 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001741 ret = -1;
1742 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001743 }
1744 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001745 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001746
Michal Vaskoe2713da2016-08-22 16:06:40 +02001747 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001748 if (!server_opts.binds) {
1749 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001750 ret = -1;
1751 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001752 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001753
Michal Vasko2e6defd2016-10-07 15:48:15 +02001754 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1755 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1756 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001757 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001758
1759 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001760#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001761 case NC_TI_LIBSSH:
1762 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1763 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1764 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001765 ret = -1;
1766 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001767 }
1768 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1769 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1770 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1771 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1772 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001773#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001774#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001775 case NC_TI_OPENSSL:
1776 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1777 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1778 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001779 ret = -1;
1780 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001781 }
1782 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001783#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001784 case NC_TI_UNIX:
1785 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1786 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1787 ERRMEM;
1788 ret = -1;
1789 goto cleanup;
1790 }
1791 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1792 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1793 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1794 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001795 default:
1796 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001797 ret = -1;
1798 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001799 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001800
Michal Vaskoade892d2017-02-22 13:40:35 +01001801cleanup:
1802 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001803 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001804
Michal Vaskoade892d2017-02-22 13:40:35 +01001805 /* BIND UNLOCK */
1806 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001807
Michal Vaskoade892d2017-02-22 13:40:35 +01001808 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001809}
1810
Michal Vasko3031aae2016-01-27 16:07:18 +01001811int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001812nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001813{
1814 struct nc_endpt *endpt;
1815 struct nc_bind *bind = NULL;
1816 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001817 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001818
Michal Vasko45e53ae2016-04-07 11:46:03 +02001819 if (!endpt_name) {
1820 ERRARG("endpt_name");
1821 return -1;
1822 } else if ((!address && !port) || (address && port)) {
1823 ERRARG("address and port");
1824 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001825 }
1826
Michal Vaskoe2713da2016-08-22 16:06:40 +02001827 if (address) {
1828 set_addr = 1;
1829 } else {
1830 set_addr = 0;
1831 }
1832
Michal Vaskoade892d2017-02-22 13:40:35 +01001833 /* BIND LOCK */
1834 pthread_mutex_lock(&server_opts.bind_lock);
1835
1836 /* ENDPT LOCK */
1837 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001838 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001839 /* BIND UNLOCK */
1840 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001841 return -1;
1842 }
1843
Michal Vaskoe2713da2016-08-22 16:06:40 +02001844 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001845
Michal Vaskoe2713da2016-08-22 16:06:40 +02001846 if (set_addr) {
1847 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001848 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001849 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001850 }
1851
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001852 if (!set_addr && endpt->ti == NC_TI_UNIX) {
1853 ret = -1;
1854 goto cleanup;
1855 }
1856
Michal Vaskoe2713da2016-08-22 16:06:40 +02001857 /* we have all the information we need to create a listening socket */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001858 if (address && (port || endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001859 /* create new socket, close the old one */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001860 if (endpt->ti == NC_TI_UNIX)
1861 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
1862 else
1863 sock = nc_sock_listen_inet(address, port);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001864 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001865 ret = -1;
1866 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001867 }
1868
1869 if (bind->sock > -1) {
1870 close(bind->sock);
1871 }
1872 bind->sock = sock;
1873 } /* else we are just setting address or port */
1874
1875 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001876 lydict_remove(server_opts.ctx, bind->address);
1877 bind->address = lydict_insert(server_opts.ctx, address, 0);
1878 } else {
1879 bind->port = port;
1880 }
1881
Michal Vaskoe2713da2016-08-22 16:06:40 +02001882 if (sock > -1) {
1883#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001884 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001885#elif defined(NC_ENABLED_SSH)
1886 VRB("Listening on %s:%u for SSH connections.", address, port);
1887#else
1888 VRB("Listening on %s:%u for TLS connections.", address, port);
1889#endif
1890 }
1891
Michal Vasko4c1fb492017-01-30 14:31:07 +01001892cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001893 /* ENDPT UNLOCK */
1894 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001895
Michal Vaskoade892d2017-02-22 13:40:35 +01001896 /* BIND UNLOCK */
1897 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001898
Michal Vasko4c1fb492017-01-30 14:31:07 +01001899 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001900}
1901
Michal Vaskoe2713da2016-08-22 16:06:40 +02001902API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001903nc_server_endpt_set_address(const char *endpt_name, const char *address)
1904{
1905 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1906}
1907
1908API int
1909nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1910{
1911 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1912}
1913
1914API int
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001915nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
1916{
1917 struct nc_endpt *endpt;
1918 uint16_t i;
1919 int ret = 0;
1920
1921 if (!endpt_name) {
1922 ERRARG("endpt_name");
1923 return -1;
1924 } else if (mode == 0) {
1925 ERRARG("mode");
1926 return -1;
1927 }
1928
1929 /* ENDPT LOCK */
1930 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
1931 if (!endpt)
1932 return -1;
1933
1934 if (endpt->ti != NC_TI_UNIX) {
1935 ret = -1;
1936 goto cleanup;
1937 }
1938
1939 endpt->opts.unixsock->mode = mode;
1940 endpt->opts.unixsock->uid = uid;
1941 endpt->opts.unixsock->gid = gid;
1942
1943cleanup:
1944 /* ENDPT UNLOCK */
1945 pthread_rwlock_unlock(&server_opts.endpt_lock);
1946
1947 return ret;
1948}
1949
1950API int
Michal Vasko59050372016-11-22 14:33:55 +01001951nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001952{
1953 uint32_t i;
1954 int ret = -1;
1955
Michal Vaskoade892d2017-02-22 13:40:35 +01001956 /* BIND LOCK */
1957 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001958
Michal Vaskoade892d2017-02-22 13:40:35 +01001959 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001960 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001961
Michal Vasko59050372016-11-22 14:33:55 +01001962 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001963 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001964 for (i = 0; i < server_opts.endpt_count; ++i) {
1965 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001966 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001967#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001968 case NC_TI_LIBSSH:
1969 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1970 free(server_opts.endpts[i].opts.ssh);
1971 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001972#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001973#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001974 case NC_TI_OPENSSL:
1975 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1976 free(server_opts.endpts[i].opts.tls);
1977 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001978#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001979 case NC_TI_UNIX:
1980 free(server_opts.endpts[i].opts.unixsock);
1981 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001982 default:
1983 ERRINT;
1984 /* won't get here ...*/
1985 break;
1986 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001987 ret = 0;
1988 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001989 free(server_opts.endpts);
1990 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001991
1992 /* remove all binds */
1993 for (i = 0; i < server_opts.endpt_count; ++i) {
1994 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1995 if (server_opts.binds[i].sock > -1) {
1996 close(server_opts.binds[i].sock);
1997 }
1998 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001999 free(server_opts.binds);
2000 server_opts.binds = NULL;
2001
Michal Vasko3031aae2016-01-27 16:07:18 +01002002 server_opts.endpt_count = 0;
2003
Michal Vasko1a38c862016-01-15 15:50:07 +01002004 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002005 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002006 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002007 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002008 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01002009 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002010 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002011#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002012 case NC_TI_LIBSSH:
2013 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2014 free(server_opts.endpts[i].opts.ssh);
2015 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002016#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002017#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002018 case NC_TI_OPENSSL:
2019 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2020 free(server_opts.endpts[i].opts.tls);
2021 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002022#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002023 case NC_TI_UNIX:
2024 free(server_opts.endpts[i].opts.unixsock);
2025 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002026 default:
2027 ERRINT;
2028 break;
2029 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002030
Michal Vaskoe2713da2016-08-22 16:06:40 +02002031 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02002032 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2033 if (server_opts.binds[i].sock > -1) {
2034 close(server_opts.binds[i].sock);
2035 }
2036
2037 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002038 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002039 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002040 free(server_opts.binds);
2041 server_opts.binds = NULL;
2042 free(server_opts.endpts);
2043 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002044 } else if (i < server_opts.endpt_count) {
2045 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2046 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002047 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002048
2049 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002050 if (name) {
2051 break;
2052 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002053 }
2054 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002055 }
2056
Michal Vaskoade892d2017-02-22 13:40:35 +01002057 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002058 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002059
Michal Vaskoade892d2017-02-22 13:40:35 +01002060 /* BIND UNLOCK */
2061 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002062
2063 return ret;
2064}
2065
Michal Vasko71090fc2016-05-24 16:37:28 +02002066API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002067nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002068{
Michal Vasko71090fc2016-05-24 16:37:28 +02002069 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002070 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002071 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002072 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002073 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002074
Michal Vasko45e53ae2016-04-07 11:46:03 +02002075 if (!server_opts.ctx) {
2076 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002077 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002078 } else if (!session) {
2079 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002080 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002081 }
2082
Michal Vaskoade892d2017-02-22 13:40:35 +01002083 /* BIND LOCK */
2084 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002085
2086 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002087 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002088 /* BIND UNLOCK */
2089 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002090 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002091 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002092
Michal Vaskoe2713da2016-08-22 16:06:40 +02002093 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002094 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002095 /* BIND UNLOCK */
2096 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002097 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002098 if (!ret) {
2099 return NC_MSG_WOULDBLOCK;
2100 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002101 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002102 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002103
2104 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2105 /* ENDPT READ LOCK */
2106 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2107
2108 /* BIND UNLOCK */
2109 pthread_mutex_unlock(&server_opts.bind_lock);
2110
Michal Vaskob48aa812016-01-18 14:13:09 +01002111 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002112
Michal Vasko131120a2018-05-29 15:44:02 +02002113 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002114 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002115 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002116 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002117 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002118 msgtype = NC_MSG_ERROR;
2119 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002120 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002121 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002122 (*session)->ctx = server_opts.ctx;
2123 (*session)->flags = NC_SESSION_SHAREDCTX;
2124 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
2125 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002126
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002127 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002128#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002129 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2130 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002131 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002132 if (ret < 0) {
2133 msgtype = NC_MSG_ERROR;
2134 goto cleanup;
2135 } else if (!ret) {
2136 msgtype = NC_MSG_WOULDBLOCK;
2137 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002138 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002139 } else
2140#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002141#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002142 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2143 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002144 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002145 if (ret < 0) {
2146 msgtype = NC_MSG_ERROR;
2147 goto cleanup;
2148 } else if (!ret) {
2149 msgtype = NC_MSG_WOULDBLOCK;
2150 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002151 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002152 } else
2153#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002154 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2155 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2156 ret = nc_accept_unix(*session, sock);
2157 if (ret < 0) {
2158 msgtype = NC_MSG_ERROR;
2159 goto cleanup;
2160 }
2161 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002162 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002163 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002164 msgtype = NC_MSG_ERROR;
2165 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002166 }
2167
Michal Vasko2cc4c682016-03-01 09:16:48 +01002168 (*session)->data = NULL;
2169
Michal Vaskoade892d2017-02-22 13:40:35 +01002170 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002171 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002172
Michal Vaskob48aa812016-01-18 14:13:09 +01002173 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002174 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002175
Michal Vasko9e036d52016-01-08 10:49:26 +01002176 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002177 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002178 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002179 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002180 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002181 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002182 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002183
2184 nc_gettimespec_mono(&ts_cur);
2185 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2186 nc_gettimespec_real(&ts_cur);
2187 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002188 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002189
Michal Vasko71090fc2016-05-24 16:37:28 +02002190 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002191
Michal Vasko71090fc2016-05-24 16:37:28 +02002192cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002193 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002194 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002195
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002196 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002197 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002198 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002199}
2200
Michal Vasko2e6defd2016-10-07 15:48:15 +02002201API int
2202nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
2203{
2204 uint16_t i;
2205
2206 if (!name) {
2207 ERRARG("name");
2208 return -1;
2209 } else if (!ti) {
2210 ERRARG("ti");
2211 return -1;
2212 }
2213
2214 /* WRITE LOCK */
2215 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2216
2217 /* check name uniqueness */
2218 for (i = 0; i < server_opts.ch_client_count; ++i) {
2219 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2220 ERR("Call Home client \"%s\" already exists.", name);
2221 /* WRITE UNLOCK */
2222 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2223 return -1;
2224 }
2225 }
2226
2227 ++server_opts.ch_client_count;
2228 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2229 if (!server_opts.ch_clients) {
2230 ERRMEM;
2231 /* WRITE UNLOCK */
2232 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2233 return -1;
2234 }
2235 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 +01002236 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 +02002237 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002238 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
2239 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002240
2241 switch (ti) {
2242#ifdef NC_ENABLED_SSH
2243 case NC_TI_LIBSSH:
2244 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2245 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2246 ERRMEM;
2247 /* WRITE UNLOCK */
2248 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2249 return -1;
2250 }
2251 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2252 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2253 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2254 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2255 break;
2256#endif
2257#ifdef NC_ENABLED_TLS
2258 case NC_TI_OPENSSL:
2259 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2260 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2261 ERRMEM;
2262 /* WRITE UNLOCK */
2263 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2264 return -1;
2265 }
2266 break;
2267#endif
2268 default:
2269 ERRINT;
2270 /* WRITE UNLOCK */
2271 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2272 return -1;
2273 }
2274
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002275 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2276
Michal Vasko2e6defd2016-10-07 15:48:15 +02002277 /* set CH default options */
2278 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2279 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2280
2281 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2282
2283 /* WRITE UNLOCK */
2284 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2285
2286 return 0;
2287}
2288
2289API int
Michal Vasko59050372016-11-22 14:33:55 +01002290nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002291{
2292 uint16_t i, j;
2293 int ret = -1;
2294
2295 /* WRITE LOCK */
2296 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2297
Michal Vasko59050372016-11-22 14:33:55 +01002298 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002299 /* remove all CH clients */
2300 for (i = 0; i < server_opts.ch_client_count; ++i) {
2301 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2302
2303 /* remove all endpoints */
2304 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2305 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2306 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2307 }
2308 free(server_opts.ch_clients[i].ch_endpts);
2309
2310 switch (server_opts.ch_clients[i].ti) {
2311#ifdef NC_ENABLED_SSH
2312 case NC_TI_LIBSSH:
2313 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2314 free(server_opts.ch_clients[i].opts.ssh);
2315 break;
2316#endif
2317#ifdef NC_ENABLED_TLS
2318 case NC_TI_OPENSSL:
2319 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2320 free(server_opts.ch_clients[i].opts.tls);
2321 break;
2322#endif
2323 default:
2324 ERRINT;
2325 /* won't get here ...*/
2326 break;
2327 }
2328
2329 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2330
2331 ret = 0;
2332 }
2333 free(server_opts.ch_clients);
2334 server_opts.ch_clients = NULL;
2335
2336 server_opts.ch_client_count = 0;
2337
2338 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002339 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002340 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002341 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 +02002342 /* remove endpt */
2343 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2344
2345 switch (server_opts.ch_clients[i].ti) {
2346#ifdef NC_ENABLED_SSH
2347 case NC_TI_LIBSSH:
2348 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2349 free(server_opts.ch_clients[i].opts.ssh);
2350 break;
2351#endif
2352#ifdef NC_ENABLED_TLS
2353 case NC_TI_OPENSSL:
2354 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2355 free(server_opts.ch_clients[i].opts.tls);
2356 break;
2357#endif
2358 default:
2359 ERRINT;
2360 break;
2361 }
2362
2363 /* remove all endpoints */
2364 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2365 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2366 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2367 }
2368 free(server_opts.ch_clients[i].ch_endpts);
2369
2370 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2371
2372 /* move last client and endpoint(s) to the empty space */
2373 --server_opts.ch_client_count;
2374 if (i < server_opts.ch_client_count) {
2375 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2376 sizeof *server_opts.ch_clients);
2377 } else if (!server_opts.ch_client_count) {
2378 free(server_opts.ch_clients);
2379 server_opts.ch_clients = NULL;
2380 }
2381
2382 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002383 if (name) {
2384 break;
2385 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002386 }
2387 }
2388 }
2389
2390 /* WRITE UNLOCK */
2391 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2392
2393 return ret;
2394}
2395
2396API int
2397nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2398{
2399 uint16_t i;
2400 struct nc_ch_client *client;
2401
2402 if (!client_name) {
2403 ERRARG("client_name");
2404 return -1;
2405 } else if (!endpt_name) {
2406 ERRARG("endpt_name");
2407 return -1;
2408 }
2409
2410 /* LOCK */
2411 client = nc_server_ch_client_lock(client_name, 0, NULL);
2412 if (!client) {
2413 return -1;
2414 }
2415
2416 for (i = 0; i < client->ch_endpt_count; ++i) {
2417 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2418 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2419 /* UNLOCK */
2420 nc_server_ch_client_unlock(client);
2421 return -1;
2422 }
2423 }
2424
2425 ++client->ch_endpt_count;
2426 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2427 if (!client->ch_endpts) {
2428 ERRMEM;
2429 /* UNLOCK */
2430 nc_server_ch_client_unlock(client);
2431 return -1;
2432 }
2433
2434 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2435 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2436 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
Frank Rimpler9f838b02018-07-25 06:44:03 +00002437 client->ch_endpts[client->ch_endpt_count - 1].sock_pending = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002438
2439 /* UNLOCK */
2440 nc_server_ch_client_unlock(client);
2441
2442 return 0;
2443}
2444
2445API int
2446nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2447{
2448 uint16_t i;
2449 int ret = -1;
2450 struct nc_ch_client *client;
2451
2452 if (!client_name) {
2453 ERRARG("client_name");
2454 return -1;
2455 }
2456
2457 /* LOCK */
2458 client = nc_server_ch_client_lock(client_name, 0, NULL);
2459 if (!client) {
2460 return -1;
2461 }
2462
2463 if (!endpt_name) {
2464 /* remove all endpoints */
2465 for (i = 0; i < client->ch_endpt_count; ++i) {
2466 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2467 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Frank Rimpler9f838b02018-07-25 06:44:03 +00002468 if (client->ch_endpts[i].sock_pending != -1) {
2469 close(client->ch_endpts[i].sock_pending);
2470 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002471 }
2472 free(client->ch_endpts);
2473 client->ch_endpts = NULL;
2474 client->ch_endpt_count = 0;
2475
2476 ret = 0;
2477 } else {
2478 for (i = 0; i < client->ch_endpt_count; ++i) {
2479 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2480 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2481 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002482
Michal Vasko4f921012016-10-20 14:07:45 +02002483 /* move last endpoint to the empty space */
2484 --client->ch_endpt_count;
2485 if (i < client->ch_endpt_count) {
2486 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2487 } else if (!server_opts.ch_client_count) {
2488 free(server_opts.ch_clients);
2489 server_opts.ch_clients = NULL;
2490 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002491
Michal Vasko4f921012016-10-20 14:07:45 +02002492 ret = 0;
2493 break;
2494 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002495 }
2496 }
2497
2498 /* UNLOCK */
2499 nc_server_ch_client_unlock(client);
2500
2501 return ret;
2502}
2503
2504API int
2505nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2506{
2507 uint16_t i;
2508 int ret = -1;
2509 struct nc_ch_client *client;
2510
2511 if (!client_name) {
2512 ERRARG("client_name");
2513 return -1;
2514 } else if (!endpt_name) {
2515 ERRARG("endpt_name");
2516 return -1;
2517 } else if (!address) {
2518 ERRARG("address");
2519 return -1;
2520 }
2521
2522 /* LOCK */
2523 client = nc_server_ch_client_lock(client_name, 0, NULL);
2524 if (!client) {
2525 return -1;
2526 }
2527
2528 for (i = 0; i < client->ch_endpt_count; ++i) {
2529 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2530 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2531 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2532
2533 ret = 0;
2534 break;
2535 }
2536 }
2537
2538 /* UNLOCK */
2539 nc_server_ch_client_unlock(client);
2540
2541 if (ret == -1) {
2542 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2543 }
2544
2545 return ret;
2546}
2547
2548API int
2549nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2550{
2551 uint16_t i;
2552 int ret = -1;
2553 struct nc_ch_client *client;
2554
2555 if (!client_name) {
2556 ERRARG("client_name");
2557 return -1;
2558 } else if (!endpt_name) {
2559 ERRARG("endpt_name");
2560 return -1;
2561 } else if (!port) {
2562 ERRARG("port");
2563 return -1;
2564 }
2565
2566 /* LOCK */
2567 client = nc_server_ch_client_lock(client_name, 0, NULL);
2568 if (!client) {
2569 return -1;
2570 }
2571
2572 for (i = 0; i < client->ch_endpt_count; ++i) {
2573 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2574 client->ch_endpts[i].port = port;
2575
2576 ret = 0;
2577 break;
2578 }
2579 }
2580
2581 /* UNLOCK */
2582 nc_server_ch_client_unlock(client);
2583
2584 if (ret == -1) {
2585 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2586 }
2587
2588 return ret;
2589}
2590
2591API int
2592nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2593{
2594 struct nc_ch_client *client;
2595
2596 if (!client_name) {
2597 ERRARG("client_name");
2598 return -1;
2599 } else if (!conn_type) {
2600 ERRARG("conn_type");
2601 return -1;
2602 }
2603
2604 /* LOCK */
2605 client = nc_server_ch_client_lock(client_name, 0, NULL);
2606 if (!client) {
2607 return -1;
2608 }
2609
2610 if (client->conn_type != conn_type) {
2611 client->conn_type = conn_type;
2612
2613 /* set default options */
2614 switch (conn_type) {
2615 case NC_CH_PERSIST:
2616 client->conn.persist.idle_timeout = 86400;
2617 client->conn.persist.ka_max_wait = 30;
2618 client->conn.persist.ka_max_attempts = 3;
2619 break;
2620 case NC_CH_PERIOD:
2621 client->conn.period.idle_timeout = 300;
2622 client->conn.period.reconnect_timeout = 60;
2623 break;
2624 default:
2625 ERRINT;
2626 break;
2627 }
2628 }
2629
2630 /* UNLOCK */
2631 nc_server_ch_client_unlock(client);
2632
2633 return 0;
2634}
2635
2636API int
2637nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2638{
2639 struct nc_ch_client *client;
2640
2641 if (!client_name) {
2642 ERRARG("client_name");
2643 return -1;
2644 }
2645
2646 /* LOCK */
2647 client = nc_server_ch_client_lock(client_name, 0, NULL);
2648 if (!client) {
2649 return -1;
2650 }
2651
2652 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002653 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002654 /* UNLOCK */
2655 nc_server_ch_client_unlock(client);
2656 return -1;
2657 }
2658
2659 client->conn.persist.idle_timeout = idle_timeout;
2660
2661 /* UNLOCK */
2662 nc_server_ch_client_unlock(client);
2663
2664 return 0;
2665}
2666
2667API int
2668nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2669{
2670 struct nc_ch_client *client;
2671
2672 if (!client_name) {
2673 ERRARG("client_name");
2674 return -1;
2675 } else if (!max_wait) {
2676 ERRARG("max_wait");
2677 return -1;
2678 }
2679
2680 /* LOCK */
2681 client = nc_server_ch_client_lock(client_name, 0, NULL);
2682 if (!client) {
2683 return -1;
2684 }
2685
2686 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002687 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002688 /* UNLOCK */
2689 nc_server_ch_client_unlock(client);
2690 return -1;
2691 }
2692
2693 client->conn.persist.ka_max_wait = max_wait;
2694
2695 /* UNLOCK */
2696 nc_server_ch_client_unlock(client);
2697
2698 return 0;
2699}
2700
2701API int
2702nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2703{
2704 struct nc_ch_client *client;
2705
2706 if (!client_name) {
2707 ERRARG("client_name");
2708 return -1;
2709 }
2710
2711 /* LOCK */
2712 client = nc_server_ch_client_lock(client_name, 0, NULL);
2713 if (!client) {
2714 return -1;
2715 }
2716
2717 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002718 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002719 /* UNLOCK */
2720 nc_server_ch_client_unlock(client);
2721 return -1;
2722 }
2723
2724 client->conn.persist.ka_max_attempts = max_attempts;
2725
2726 /* UNLOCK */
2727 nc_server_ch_client_unlock(client);
2728
2729 return 0;
2730}
2731
2732API int
2733nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2734{
2735 struct nc_ch_client *client;
2736
2737 if (!client_name) {
2738 ERRARG("client_name");
2739 return -1;
2740 }
2741
2742 /* LOCK */
2743 client = nc_server_ch_client_lock(client_name, 0, NULL);
2744 if (!client) {
2745 return -1;
2746 }
2747
2748 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002749 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002750 /* UNLOCK */
2751 nc_server_ch_client_unlock(client);
2752 return -1;
2753 }
2754
2755 client->conn.period.idle_timeout = idle_timeout;
2756
2757 /* UNLOCK */
2758 nc_server_ch_client_unlock(client);
2759
2760 return 0;
2761}
2762
2763API int
2764nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2765{
2766 struct nc_ch_client *client;
2767
2768 if (!client_name) {
2769 ERRARG("client_name");
2770 return -1;
2771 } else if (!reconnect_timeout) {
2772 ERRARG("reconnect_timeout");
2773 return -1;
2774 }
2775
2776 /* LOCK */
2777 client = nc_server_ch_client_lock(client_name, 0, NULL);
2778 if (!client) {
2779 return -1;
2780 }
2781
2782 if (client->conn_type != NC_CH_PERIOD) {
2783 ERR("Call Home client \"%s\" is not of periodic connection type.");
2784 /* UNLOCK */
2785 nc_server_ch_client_unlock(client);
2786 return -1;
2787 }
2788
2789 client->conn.period.reconnect_timeout = reconnect_timeout;
2790
2791 /* UNLOCK */
2792 nc_server_ch_client_unlock(client);
2793
2794 return 0;
2795}
2796
2797API int
2798nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2799{
2800 struct nc_ch_client *client;
2801
2802 if (!client_name) {
2803 ERRARG("client_name");
2804 return -1;
2805 }
2806
2807 /* LOCK */
2808 client = nc_server_ch_client_lock(client_name, 0, NULL);
2809 if (!client) {
2810 return -1;
2811 }
2812
2813 client->start_with = start_with;
2814
2815 /* UNLOCK */
2816 nc_server_ch_client_unlock(client);
2817
2818 return 0;
2819}
2820
2821API int
2822nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2823{
2824 struct nc_ch_client *client;
2825
2826 if (!client_name) {
2827 ERRARG("client_name");
2828 return -1;
2829 } else if (!max_attempts) {
2830 ERRARG("max_attempts");
2831 return -1;
2832 }
2833
2834 /* LOCK */
2835 client = nc_server_ch_client_lock(client_name, 0, NULL);
2836 if (!client) {
2837 return -1;
2838 }
2839
2840 client->max_attempts = max_attempts;
2841
2842 /* UNLOCK */
2843 nc_server_ch_client_unlock(client);
2844
2845 return 0;
2846}
2847
2848/* client lock is expected to be held */
2849static NC_MSG_TYPE
2850nc_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 +01002851{
Michal Vasko71090fc2016-05-24 16:37:28 +02002852 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002853 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002854 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01002855 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01002856
Michal Vasko66032bc2019-01-22 15:03:12 +01002857 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002858 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002859 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002860 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00002861 /* no need to store the socket as pending any longer */
2862 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01002863
Michal Vasko131120a2018-05-29 15:44:02 +02002864 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002865 if (!(*session)) {
2866 ERRMEM;
2867 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01002868 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002869 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002870 }
2871 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01002872 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002873 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko66032bc2019-01-22 15:03:12 +01002874 (*session)->host = lydict_insert_zc(server_opts.ctx, ip_host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002875 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002876
Michal Vaskob05053d2016-01-22 16:12:06 +01002877 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002878#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002879 if (client->ti == NC_TI_LIBSSH) {
2880 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002881 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002882 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002883
Michal Vasko71090fc2016-05-24 16:37:28 +02002884 if (ret < 0) {
2885 msgtype = NC_MSG_ERROR;
2886 goto fail;
2887 } else if (!ret) {
2888 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002889 goto fail;
2890 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002891 } else
2892#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002893#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002894 if (client->ti == NC_TI_OPENSSL) {
2895 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002896 ret = nc_accept_tls_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
2908 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002909 ERRINT;
2910 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002911 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002912 goto fail;
2913 }
2914
2915 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002916 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01002917
2918 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002919 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002920 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002921 goto fail;
2922 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002923
2924 nc_gettimespec_mono(&ts_cur);
2925 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2926 nc_gettimespec_real(&ts_cur);
2927 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01002928 (*session)->status = NC_STATUS_RUNNING;
2929
Michal Vasko71090fc2016-05-24 16:37:28 +02002930 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002931
2932fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002933 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002934 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002935 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002936}
2937
Michal Vasko2e6defd2016-10-07 15:48:15 +02002938struct nc_ch_client_thread_arg {
2939 char *client_name;
2940 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2941};
2942
2943static struct nc_ch_client *
2944nc_server_ch_client_with_endpt_lock(const char *name)
2945{
2946 struct nc_ch_client *client;
2947
2948 while (1) {
2949 /* LOCK */
2950 client = nc_server_ch_client_lock(name, 0, NULL);
2951 if (!client) {
2952 return NULL;
2953 }
2954 if (client->ch_endpt_count) {
2955 return client;
2956 }
2957 /* no endpoints defined yet */
2958
2959 /* UNLOCK */
2960 nc_server_ch_client_unlock(client);
2961
2962 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2963 }
2964
2965 return NULL;
2966}
2967
2968static int
2969nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2970{
Michal Vasko3f05a092018-03-13 10:39:49 +01002971 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002972 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002973 struct timespec ts;
2974 struct nc_ch_client *client;
2975
2976 /* session created, initialize condition */
Michal Vasko27377422018-03-15 08:59:35 +01002977 session->opts.server.ch_lock = calloc(1, sizeof *session->opts.server.ch_lock);
2978 session->opts.server.ch_cond = calloc(1, sizeof *session->opts.server.ch_cond);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002979 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2980 ERRMEM;
2981 nc_session_free(session, NULL);
2982 return -1;
2983 }
2984 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2985 pthread_cond_init(session->opts.server.ch_cond, NULL);
2986
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002987 session->flags |= NC_SESSION_CALLHOME;
2988
Michal Vasko2e6defd2016-10-07 15:48:15 +02002989 /* CH LOCK */
2990 pthread_mutex_lock(session->opts.server.ch_lock);
2991
2992 /* give the session to the user */
2993 data->session_clb(data->client_name, session);
2994
2995 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02002996 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01002997 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002998
Michal Vasko3f05a092018-03-13 10:39:49 +01002999 r = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
3000 if (!r) {
3001 /* we were woken up, something probably happened */
3002 if (session->status != NC_STATUS_RUNNING) {
3003 break;
3004 }
3005 } else if (r != ETIMEDOUT) {
3006 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3007 ret = -1;
3008 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003009 }
3010
Michal Vasko2e6defd2016-10-07 15:48:15 +02003011 /* check whether the client was not removed */
3012 /* LOCK */
3013 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
3014 if (!client) {
3015 /* client was removed, finish thread */
3016 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
3017 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003018 ret = 1;
3019 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003020 }
3021
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003022 if (client->conn_type == NC_CH_PERSIST) {
3023 /* TODO keep-alives */
3024 idle_timeout = client->conn.persist.idle_timeout;
3025 } else {
3026 idle_timeout = client->conn.period.idle_timeout;
3027 }
3028
Michal Vasko9fb42272017-10-05 13:50:05 +02003029 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003030 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 +02003031 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3032 session->status = NC_STATUS_INVALID;
3033 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3034 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003035
3036 /* UNLOCK */
3037 nc_server_ch_client_unlock(client);
3038
3039 } while (session->status == NC_STATUS_RUNNING);
3040
Michal Vasko27377422018-03-15 08:59:35 +01003041 /* CH UNLOCK */
3042 pthread_mutex_unlock(session->opts.server.ch_lock);
3043
Michal Vasko3f05a092018-03-13 10:39:49 +01003044 if (session->status == NC_STATUS_CLOSING) {
3045 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
3046 session->flags &= ~NC_SESSION_CALLHOME;
3047 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003048
Michal Vasko3f05a092018-03-13 10:39:49 +01003049 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003050}
3051
3052static void *
3053nc_ch_client_thread(void *arg)
3054{
3055 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3056 NC_MSG_TYPE msgtype;
3057 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003058 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003059 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003060 struct nc_ch_endpt *cur_endpt;
3061 struct nc_session *session;
3062 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003063 uint32_t client_id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003064
3065 /* LOCK */
3066 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3067 if (!client) {
3068 goto cleanup;
3069 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003070 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003071
3072 cur_endpt = &client->ch_endpts[0];
3073 cur_endpt_name = strdup(cur_endpt->name);
3074
Michal Vasko29af44b2016-10-13 10:59:55 +02003075 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003076 while (1) {
3077 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
3078
3079 if (msgtype == NC_MSG_HELLO) {
3080 /* UNLOCK */
3081 nc_server_ch_client_unlock(client);
3082
Michal Vasko29af44b2016-10-13 10:59:55 +02003083 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003084 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3085 goto cleanup;
3086 }
Michal Vasko29af44b2016-10-13 10:59:55 +02003087 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003088
3089 /* LOCK */
3090 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3091 if (!client) {
3092 goto cleanup;
3093 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003094 if (client->id != client_id) {
3095 nc_server_ch_client_unlock(client);
3096 goto cleanup;
3097 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003098
3099 /* session changed status -> it was disconnected for whatever reason,
3100 * persistent connection immediately tries to reconnect, periodic waits some first */
3101 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003102 /* UNLOCK */
3103 nc_server_ch_client_unlock(client);
3104
3105 /* TODO wake up sometimes to check for new notifications */
3106 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
3107
3108 /* LOCK */
3109 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3110 if (!client) {
3111 goto cleanup;
3112 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003113 if (client->id != client_id) {
3114 nc_server_ch_client_unlock(client);
3115 goto cleanup;
3116 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003117 }
3118
3119 /* set next endpoint to try */
3120 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003121 next_endpt_index = 0;
Michal Vasko2a225342018-09-05 08:38:34 +02003122 } else {
Peter Feiged05f2252018-09-03 08:09:47 +00003123 /* we keep the current one but due to unlock/lock we have to find it again */
3124 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3125 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3126 break;
3127 }
3128 }
3129 if (next_endpt_index >= client->ch_endpt_count) {
3130 /* endpoint was removed, start with the first one */
3131 next_endpt_index = 0;
3132 }
3133 }
3134
Michal Vasko2e6defd2016-10-07 15:48:15 +02003135 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003136 /* UNLOCK */
3137 nc_server_ch_client_unlock(client);
3138
Michal Vasko2e6defd2016-10-07 15:48:15 +02003139 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003140 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3141
Michal Vasko6bb116b2016-10-26 13:53:46 +02003142 /* LOCK */
3143 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3144 if (!client) {
3145 goto cleanup;
3146 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003147 if (client->id != client_id) {
3148 nc_server_ch_client_unlock(client);
3149 goto cleanup;
3150 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003151
Michal Vasko2e6defd2016-10-07 15:48:15 +02003152 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003153
3154 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003155 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3156 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003157 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003158 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003159 }
3160
Peter Feiged05f2252018-09-03 08:09:47 +00003161 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003162 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003163 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003164 cur_attempts = 0;
3165 } else if (cur_attempts == client->max_attempts) {
3166 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003167 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003168 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003169 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003170 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003171 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003172 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003173 }
3174
3175 cur_attempts = 0;
3176 } /* else we keep the current one */
3177 }
Peter Feiged05f2252018-09-03 08:09:47 +00003178
3179 cur_endpt = &client->ch_endpts[next_endpt_index];
3180 free(cur_endpt_name);
3181 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003182 }
3183
3184cleanup:
3185 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003186 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003187 free(data->client_name);
3188 free(data);
3189 return NULL;
3190}
3191
3192API int
3193nc_connect_ch_client_dispatch(const char *client_name,
Michal Vasko3f05a092018-03-13 10:39:49 +01003194 void (*session_clb)(const char *client_name, struct nc_session *new_session))
3195{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003196 int ret;
3197 pthread_t tid;
3198 struct nc_ch_client_thread_arg *arg;
3199
3200 if (!client_name) {
3201 ERRARG("client_name");
3202 return -1;
3203 } else if (!session_clb) {
3204 ERRARG("session_clb");
3205 return -1;
3206 }
3207
3208 arg = malloc(sizeof *arg);
3209 if (!arg) {
3210 ERRMEM;
3211 return -1;
3212 }
3213 arg->client_name = strdup(client_name);
3214 if (!arg->client_name) {
3215 ERRMEM;
3216 free(arg);
3217 return -1;
3218 }
3219 arg->session_clb = session_clb;
3220
3221 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3222 if (ret) {
3223 ERR("Creating a new thread failed (%s).", strerror(ret));
3224 free(arg->client_name);
3225 free(arg);
3226 return -1;
3227 }
3228 /* the thread now manages arg */
3229
3230 pthread_detach(tid);
3231
3232 return 0;
3233}
3234
Radek Krejci53691be2016-02-22 13:58:37 +01003235#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003236
Michal Vaskoe8e07702017-03-15 10:19:30 +01003237API int
3238nc_server_endpt_count(void)
3239{
3240 return server_opts.endpt_count;
3241}
3242
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003243API time_t
3244nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003245{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003246 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003247 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003248 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003249 }
3250
Michal Vasko2e6defd2016-10-07 15:48:15 +02003251 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003252}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003253
3254API void
3255nc_session_set_notif_status(struct nc_session *session, int notif_status)
3256{
3257 if (!session || (session->side != NC_SERVER)) {
3258 ERRARG("session");
3259 return;
3260 }
3261
3262 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3263}
3264
3265API int
3266nc_session_get_notif_status(const struct nc_session *session)
3267{
3268 if (!session || (session->side != NC_SERVER)) {
3269 ERRARG("session");
3270 return 0;
3271 }
3272
3273 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003274}
Michal Vasko8f430592019-02-26 08:32:54 +01003275
3276API int
3277nc_session_is_callhome(const struct nc_session *session)
3278{
3279 if (!session || (session->side != NC_SERVER)) {
3280 ERRARG("session");
3281 return 0;
3282 }
3283
3284 if (session->flags & NC_SESSION_CALLHOME) {
3285 return 1;
3286 }
3287
3288 return 0;
3289}