blob: 636b1a2fe4f2830395d42ca29951e96ae45d2f31 [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{
Michal Vasko47003942019-03-14 12:25:23 +01001129 uint8_t q_id;
1130 uint16_t session_count;
1131
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001132 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001133 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001134 return 0;
1135 }
1136
Michal Vasko47003942019-03-14 12:25:23 +01001137 /* LOCK (just for memory barrier so that we read the current value) */
1138 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1139 return 0;
1140 }
1141
1142 session_count = ps->session_count;
1143
1144 /* UNLOCK */
1145 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1146
1147 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001148}
1149
Michal Vasko131120a2018-05-29 15:44:02 +02001150/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001151 * returns: NC_PSPOLL_ERROR,
1152 * NC_PSPOLL_BAD_RPC,
1153 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1154 * NC_PSPOLL_RPC
1155 */
1156static int
Michal Vasko131120a2018-05-29 15:44:02 +02001157nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001158{
1159 struct lyxml_elem *xml = NULL;
1160 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001161 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001162 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001163
Michal Vasko45e53ae2016-04-07 11:46:03 +02001164 if (!session) {
1165 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001166 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001167 } else if (!rpc) {
1168 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001169 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001170 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001171 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001172 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001173 }
1174
Michal Vasko131120a2018-05-29 15:44:02 +02001175 msgtype = nc_read_msg_io(session, io_timeout, &xml, 0);
Michal Vasko428087d2016-01-14 16:04:28 +01001176
1177 switch (msgtype) {
1178 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001179 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001180 if (!*rpc) {
1181 ERRMEM;
1182 goto error;
1183 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001184
Radek Krejcif93c7d42016-04-06 13:41:15 +02001185 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001186 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1187 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001188 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001189 /* parsing RPC failed */
Michal Vaskoc9970242018-02-14 16:03:35 +01001190 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
Michal Vasko131120a2018-05-29 15:44:02 +02001191 ret = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001192 nc_server_reply_free(reply);
1193 if (ret == -1) {
1194 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001195 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001196 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1197 } else {
1198 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001199 }
Michal Vasko428087d2016-01-14 16:04:28 +01001200 (*rpc)->root = xml;
1201 break;
1202 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001203 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001204 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001205 goto error;
1206 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001207 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001208 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001209 goto error;
1210 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001211 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001212 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001213 goto error;
1214 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001215 /* NC_MSG_ERROR,
Michal Vasko131120a2018-05-29 15:44:02 +02001216 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg_io()
Michal Vasko428087d2016-01-14 16:04:28 +01001217 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001218 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001219 break;
1220 }
1221
Michal Vasko71090fc2016-05-24 16:37:28 +02001222 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001223
1224error:
1225 /* cleanup */
1226 lyxml_free(server_opts.ctx, xml);
1227
Michal Vasko71090fc2016-05-24 16:37:28 +02001228 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001229}
1230
fanchanghu966f2de2016-07-21 02:28:57 -04001231API void
1232nc_set_global_rpc_clb(nc_rpc_clb clb)
1233{
1234 global_rpc_clb = clb;
1235}
1236
Radek Krejci93e80222016-10-03 13:34:25 +02001237API NC_MSG_TYPE
1238nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1239{
Michal Vasko131120a2018-05-29 15:44:02 +02001240 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001241
1242 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001243 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001244 ERRARG("session");
1245 return NC_MSG_ERROR;
1246 } else if (!notif || !notif->tree || !notif->eventtime) {
1247 ERRARG("notif");
1248 return NC_MSG_ERROR;
1249 }
1250
Michal Vasko131120a2018-05-29 15:44:02 +02001251 /* we do not need RPC lock for this, IO lock will be acquired properly */
1252 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
1253 if (ret == NC_MSG_ERROR) {
Radek Krejci93e80222016-10-03 13:34:25 +02001254 ERR("Session %u: failed to write notification.", session->id);
Radek Krejci93e80222016-10-03 13:34:25 +02001255 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001256
Michal Vasko131120a2018-05-29 15:44:02 +02001257 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001258}
1259
Michal Vasko131120a2018-05-29 15:44:02 +02001260/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001261 * returns: NC_PSPOLL_ERROR,
1262 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1263 * NC_PSPOLL_REPLY_ERROR,
1264 * 0
1265 */
1266static int
Michal Vasko131120a2018-05-29 15:44:02 +02001267nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001268{
1269 nc_rpc_clb clb;
1270 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001271 struct lys_node *rpc_act = NULL;
1272 struct lyd_node *next, *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001273 int ret = 0;
1274 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001275
Michal Vasko4a827e52016-03-03 10:59:00 +01001276 if (!rpc) {
1277 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001278 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001279 }
1280
Michal Vasko90e8e692016-07-13 12:27:57 +02001281 if (rpc->tree->schema->nodetype == LYS_RPC) {
1282 /* RPC */
1283 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001284 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001285 /* action */
1286 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1287 if (elem->schema->nodetype == LYS_ACTION) {
1288 rpc_act = elem->schema;
1289 break;
1290 }
1291 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001292 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001293 if (!rpc_act) {
1294 ERRINT;
1295 return NC_PSPOLL_ERROR;
1296 }
1297 }
1298
1299 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001300 if (!global_rpc_clb) {
1301 /* no callback, reply with a not-implemented error */
1302 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
1303 } else {
1304 reply = global_rpc_clb(rpc->tree, session);
1305 }
Michal Vasko428087d2016-01-14 16:04:28 +01001306 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001307 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001308 reply = clb(rpc->tree, session);
1309 }
1310
1311 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001312 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001313 }
Michal Vasko131120a2018-05-29 15:44:02 +02001314 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->root, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001315 if (reply->type == NC_RPL_ERROR) {
1316 ret |= NC_PSPOLL_REPLY_ERROR;
1317 }
1318 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001319
Michal Vasko131120a2018-05-29 15:44:02 +02001320 if (r != NC_MSG_REPLY) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001321 ERR("Session %u: failed to write reply.", session->id);
1322 ret |= NC_PSPOLL_ERROR;
1323 }
Michal Vasko428087d2016-01-14 16:04:28 +01001324
1325 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1326 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1327 session->status = NC_STATUS_INVALID;
1328 }
1329
Michal Vasko71090fc2016-05-24 16:37:28 +02001330 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001331}
1332
Michal Vasko131120a2018-05-29 15:44:02 +02001333/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001334 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1335 * NC_PSPOLL_ERROR, (msg filled)
1336 * NC_PSPOLL_TIMEOUT,
1337 * NC_PSPOLL_RPC (some application data available),
1338 * NC_PSPOLL_SSH_CHANNEL,
1339 * NC_PSPOLL_SSH_MSG
1340 */
1341static int
Michal Vasko131120a2018-05-29 15:44:02 +02001342nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001343{
Michal Vasko9a327362017-01-11 11:31:46 +01001344 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001345 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001346#ifdef NC_ENABLED_SSH
1347 struct nc_session *new;
1348#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001349
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001350 /* check timeout first */
1351 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001352 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001353 sprintf(msg, "session idle timeout elapsed");
1354 session->status = NC_STATUS_INVALID;
1355 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1356 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1357 }
1358
Michal Vasko131120a2018-05-29 15:44:02 +02001359 r = nc_session_io_lock(session, io_timeout, __func__);
1360 if (r < 0) {
1361 sprintf(msg, "session IO lock failed to be acquired");
1362 return NC_PSPOLL_ERROR;
1363 } else if (!r) {
1364 return NC_PSPOLL_TIMEOUT;
1365 }
1366
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001367 switch (session->ti_type) {
1368#ifdef NC_ENABLED_SSH
1369 case NC_TI_LIBSSH:
1370 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001371 if (r == SSH_EOF) {
1372 sprintf(msg, "SSH channel unexpected EOF");
1373 session->status = NC_STATUS_INVALID;
1374 session->term_reason = NC_SESSION_TERM_DROPPED;
1375 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1376 } else if (r == SSH_ERROR) {
1377 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001378 session->status = NC_STATUS_INVALID;
1379 session->term_reason = NC_SESSION_TERM_OTHER;
1380 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001381 } else if (!r) {
1382 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1383 /* new SSH message */
1384 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1385 if (session->ti.libssh.next) {
1386 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1387 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1388 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1389 /* new NETCONF SSH channel */
1390 ret = NC_PSPOLL_SSH_CHANNEL;
1391 break;
1392 }
1393 }
1394 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001395 break;
1396 }
1397 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001398
Michal Vasko8dcaa882017-10-19 14:28:42 +02001399 /* just some SSH message */
1400 ret = NC_PSPOLL_SSH_MSG;
1401 } else {
1402 ret = NC_PSPOLL_TIMEOUT;
1403 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001404 } else {
1405 /* we have some application data */
1406 ret = NC_PSPOLL_RPC;
1407 }
1408 break;
1409#endif
1410#ifdef NC_ENABLED_TLS
1411 case NC_TI_OPENSSL:
1412 r = SSL_pending(session->ti.tls);
1413 if (!r) {
1414 /* no data pending in the SSL buffer, poll fd */
1415 pfd.fd = SSL_get_rfd(session->ti.tls);
1416 if (pfd.fd < 0) {
1417 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1418 ret = NC_PSPOLL_ERROR;
1419 break;
1420 }
1421 pfd.events = POLLIN;
1422 pfd.revents = 0;
1423 r = poll(&pfd, 1, 0);
1424
1425 if ((r < 0) && (errno != EINTR)) {
1426 sprintf(msg, "poll failed (%s)", strerror(errno));
1427 session->status = NC_STATUS_INVALID;
1428 ret = NC_PSPOLL_ERROR;
1429 } else if (r > 0) {
1430 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1431 sprintf(msg, "communication socket unexpectedly closed");
1432 session->status = NC_STATUS_INVALID;
1433 session->term_reason = NC_SESSION_TERM_DROPPED;
1434 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1435 } else if (pfd.revents & POLLERR) {
1436 sprintf(msg, "communication socket error");
1437 session->status = NC_STATUS_INVALID;
1438 session->term_reason = NC_SESSION_TERM_OTHER;
1439 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1440 } else {
1441 ret = NC_PSPOLL_RPC;
1442 }
1443 } else {
1444 ret = NC_PSPOLL_TIMEOUT;
1445 }
1446 } else {
1447 ret = NC_PSPOLL_RPC;
1448 }
1449 break;
1450#endif
1451 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001452 case NC_TI_UNIX:
1453 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001454 pfd.events = POLLIN;
1455 pfd.revents = 0;
1456 r = poll(&pfd, 1, 0);
1457
1458 if ((r < 0) && (errno != EINTR)) {
1459 sprintf(msg, "poll failed (%s)", strerror(errno));
1460 session->status = NC_STATUS_INVALID;
1461 ret = NC_PSPOLL_ERROR;
1462 } else if (r > 0) {
1463 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1464 sprintf(msg, "communication socket unexpectedly closed");
1465 session->status = NC_STATUS_INVALID;
1466 session->term_reason = NC_SESSION_TERM_DROPPED;
1467 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1468 } else if (pfd.revents & POLLERR) {
1469 sprintf(msg, "communication socket error");
1470 session->status = NC_STATUS_INVALID;
1471 session->term_reason = NC_SESSION_TERM_OTHER;
1472 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1473 } else {
1474 ret = NC_PSPOLL_RPC;
1475 }
1476 } else {
1477 ret = NC_PSPOLL_TIMEOUT;
1478 }
1479 break;
1480 case NC_TI_NONE:
1481 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1482 ret = NC_PSPOLL_ERROR;
1483 break;
1484 }
1485
Michal Vasko131120a2018-05-29 15:44:02 +02001486 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001487 return ret;
1488}
1489
1490API int
1491nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1492{
1493 int ret, r;
1494 uint8_t q_id;
1495 uint16_t i, j;
1496 char msg[256];
1497 struct timespec ts_timeout, ts_cur;
1498 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001499 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001500 struct nc_server_rpc *rpc = NULL;
1501
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001502 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001503 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001504 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001505 }
1506
Michal Vaskoade892d2017-02-22 13:40:35 +01001507 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001508 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001509 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001510 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001511
Michal Vaskoade892d2017-02-22 13:40:35 +01001512 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001513 nc_ps_unlock(ps, q_id, __func__);
1514 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001515 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001516
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001517 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001518 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001519 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001520 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001521 nc_addtimespec(&ts_timeout, timeout);
1522 }
1523
1524 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001525 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001526 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001527 if (ps->last_event_session == ps->session_count - 1) {
1528 i = j = 0;
1529 } else {
1530 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001531 }
Michal Vasko9a327362017-01-11 11:31:46 +01001532 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001533 cur_ps_session = ps->sessions[i];
1534 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001535
Michal Vasko131120a2018-05-29 15:44:02 +02001536 /* SESSION RPC LOCK */
1537 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001538 if (r == -1) {
1539 ret = NC_PSPOLL_ERROR;
1540 } else if (r == 1) {
1541 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001542 switch (cur_ps_session->state) {
1543 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001544 if (cur_session->status == NC_STATUS_RUNNING) {
1545 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001546 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001547
Michal Vasko131120a2018-05-29 15:44:02 +02001548 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001549 switch (ret) {
1550 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1551 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001552 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001553 break;
1554 case NC_PSPOLL_ERROR:
1555 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001556 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001557 break;
1558 case NC_PSPOLL_TIMEOUT:
1559#ifdef NC_ENABLED_SSH
1560 case NC_PSPOLL_SSH_CHANNEL:
1561 case NC_PSPOLL_SSH_MSG:
1562#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001563 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001564 break;
1565 case NC_PSPOLL_RPC:
1566 /* let's keep the state busy, we are not done with this session */
1567 break;
1568 }
1569 } else {
1570 /* session is not fine, let the caller know */
1571 ret = NC_PSPOLL_SESSION_TERM;
1572 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1573 ret |= NC_PSPOLL_SESSION_ERROR;
1574 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001575 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001576 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001577 break;
1578 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001579 /* it definitely should not be busy because we have the lock */
1580 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001581 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001582 break;
1583 case NC_PS_STATE_INVALID:
1584 /* we got it locked, but it will be freed, let it be */
1585 ret = NC_PSPOLL_TIMEOUT;
1586 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001587 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001588
Michal Vasko131120a2018-05-29 15:44:02 +02001589 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001590 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001591 /* SESSION RPC UNLOCK */
1592 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001593 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001594 } else {
1595 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001596 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001597 }
Michal Vasko428087d2016-01-14 16:04:28 +01001598
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001599 /* something happened */
1600 if (ret != NC_PSPOLL_TIMEOUT) {
1601 break;
1602 }
1603
Michal Vasko9a327362017-01-11 11:31:46 +01001604 if (i == ps->session_count - 1) {
1605 i = 0;
1606 } else {
1607 ++i;
1608 }
1609 } while (i != j);
1610
Michal Vaskoade892d2017-02-22 13:40:35 +01001611 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001612 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001613 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001614 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001615 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001616
1617 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1618 /* final timeout */
1619 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001620 }
Michal Vasko428087d2016-01-14 16:04:28 +01001621 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001622 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001623
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001624 /* do we want to return the session? */
1625 switch (ret) {
1626 case NC_PSPOLL_RPC:
1627 case NC_PSPOLL_SESSION_TERM:
1628 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1629#ifdef NC_ENABLED_SSH
1630 case NC_PSPOLL_SSH_CHANNEL:
1631 case NC_PSPOLL_SSH_MSG:
1632#endif
1633 if (session) {
1634 *session = cur_session;
1635 }
1636 ps->last_event_session = i;
1637 break;
1638 default:
1639 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001640 }
Michal Vasko428087d2016-01-14 16:04:28 +01001641
Michal Vaskoade892d2017-02-22 13:40:35 +01001642 /* PS UNLOCK */
1643 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001644
Michal Vasko131120a2018-05-29 15:44:02 +02001645 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001646 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001647 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001648 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1649 if (cur_session->status != NC_STATUS_RUNNING) {
1650 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001651 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001652 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001653 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001654 }
1655 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001656 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001657
Michal Vasko7f1ee932018-10-11 09:41:42 +02001658 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001659 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001660 if (cur_session->status != NC_STATUS_RUNNING) {
1661 ret |= NC_PSPOLL_SESSION_TERM;
1662 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1663 ret |= NC_PSPOLL_SESSION_ERROR;
1664 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001665 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001666 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001667 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001668 }
Michal Vasko428087d2016-01-14 16:04:28 +01001669 }
Michal Vasko7f1ee932018-10-11 09:41:42 +02001670 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001671
Michal Vasko131120a2018-05-29 15:44:02 +02001672 /* SESSION RPC UNLOCK */
1673 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001674 }
1675
Michal Vasko48a63ed2016-03-01 09:48:21 +01001676 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001677}
1678
Michal Vaskod09eae62016-02-01 10:32:52 +01001679API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001680nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001681{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001682 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001683 uint16_t i;
1684 struct nc_session *session;
1685
Michal Vasko9a25e932016-02-01 10:36:42 +01001686 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001687 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001688 return;
1689 }
1690
Michal Vasko48a63ed2016-03-01 09:48:21 +01001691 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001692 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001693 return;
1694 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001695
Michal Vasko48a63ed2016-03-01 09:48:21 +01001696 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001697 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001698 nc_session_free(ps->sessions[i]->session, data_free);
1699 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001700 }
1701 free(ps->sessions);
1702 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001703 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001704 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001705 } else {
1706 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001707 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1708 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001709 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001710 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001711 continue;
1712 }
1713
1714 ++i;
1715 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001716 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001717
1718 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001719 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001720}
1721
Radek Krejci53691be2016-02-22 13:58:37 +01001722#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001723
Michal Vaskoe2713da2016-08-22 16:06:40 +02001724API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001725nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001726{
Michal Vasko3031aae2016-01-27 16:07:18 +01001727 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001728 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001729
Michal Vasko45e53ae2016-04-07 11:46:03 +02001730 if (!name) {
1731 ERRARG("name");
1732 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001733 }
1734
Michal Vaskoade892d2017-02-22 13:40:35 +01001735 /* BIND LOCK */
1736 pthread_mutex_lock(&server_opts.bind_lock);
1737
1738 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001739 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001740
1741 /* check name uniqueness */
1742 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001743 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001744 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001745 ret = -1;
1746 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001747 }
1748 }
1749
Michal Vasko3031aae2016-01-27 16:07:18 +01001750 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001751 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001752 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001753 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001754 ret = -1;
1755 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001756 }
1757 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001758 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001759
Michal Vaskoe2713da2016-08-22 16:06:40 +02001760 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001761 if (!server_opts.binds) {
1762 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001763 ret = -1;
1764 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001765 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001766
Michal Vasko2e6defd2016-10-07 15:48:15 +02001767 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1768 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1769 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001770 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001771
1772 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001773#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001774 case NC_TI_LIBSSH:
1775 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1776 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1777 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001778 ret = -1;
1779 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001780 }
1781 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1782 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1783 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1784 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1785 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001786#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001787#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001788 case NC_TI_OPENSSL:
1789 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1790 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1791 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001792 ret = -1;
1793 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001794 }
1795 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001796#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001797 case NC_TI_UNIX:
1798 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1799 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1800 ERRMEM;
1801 ret = -1;
1802 goto cleanup;
1803 }
1804 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1805 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1806 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1807 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001808 default:
1809 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001810 ret = -1;
1811 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001812 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001813
Michal Vaskoade892d2017-02-22 13:40:35 +01001814cleanup:
1815 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001816 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001817
Michal Vaskoade892d2017-02-22 13:40:35 +01001818 /* BIND UNLOCK */
1819 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001820
Michal Vaskoade892d2017-02-22 13:40:35 +01001821 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001822}
1823
Michal Vasko3031aae2016-01-27 16:07:18 +01001824int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001825nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001826{
1827 struct nc_endpt *endpt;
1828 struct nc_bind *bind = NULL;
1829 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001830 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001831
Michal Vasko45e53ae2016-04-07 11:46:03 +02001832 if (!endpt_name) {
1833 ERRARG("endpt_name");
1834 return -1;
1835 } else if ((!address && !port) || (address && port)) {
1836 ERRARG("address and port");
1837 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001838 }
1839
Michal Vaskoe2713da2016-08-22 16:06:40 +02001840 if (address) {
1841 set_addr = 1;
1842 } else {
1843 set_addr = 0;
1844 }
1845
Michal Vaskoade892d2017-02-22 13:40:35 +01001846 /* BIND LOCK */
1847 pthread_mutex_lock(&server_opts.bind_lock);
1848
1849 /* ENDPT LOCK */
1850 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001851 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001852 /* BIND UNLOCK */
1853 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001854 return -1;
1855 }
1856
Michal Vaskoe2713da2016-08-22 16:06:40 +02001857 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001858
Michal Vaskoe2713da2016-08-22 16:06:40 +02001859 if (set_addr) {
1860 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001861 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001862 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001863 }
1864
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001865 if (!set_addr && endpt->ti == NC_TI_UNIX) {
1866 ret = -1;
1867 goto cleanup;
1868 }
1869
Michal Vaskoe2713da2016-08-22 16:06:40 +02001870 /* we have all the information we need to create a listening socket */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001871 if (address && (port || endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001872 /* create new socket, close the old one */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001873 if (endpt->ti == NC_TI_UNIX)
1874 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
1875 else
1876 sock = nc_sock_listen_inet(address, port);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001877 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001878 ret = -1;
1879 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001880 }
1881
1882 if (bind->sock > -1) {
1883 close(bind->sock);
1884 }
1885 bind->sock = sock;
1886 } /* else we are just setting address or port */
1887
1888 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001889 lydict_remove(server_opts.ctx, bind->address);
1890 bind->address = lydict_insert(server_opts.ctx, address, 0);
1891 } else {
1892 bind->port = port;
1893 }
1894
Michal Vaskoe2713da2016-08-22 16:06:40 +02001895 if (sock > -1) {
1896#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001897 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001898#elif defined(NC_ENABLED_SSH)
1899 VRB("Listening on %s:%u for SSH connections.", address, port);
1900#else
1901 VRB("Listening on %s:%u for TLS connections.", address, port);
1902#endif
1903 }
1904
Michal Vasko4c1fb492017-01-30 14:31:07 +01001905cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001906 /* ENDPT UNLOCK */
1907 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001908
Michal Vaskoade892d2017-02-22 13:40:35 +01001909 /* BIND UNLOCK */
1910 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001911
Michal Vasko4c1fb492017-01-30 14:31:07 +01001912 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001913}
1914
Michal Vaskoe2713da2016-08-22 16:06:40 +02001915API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001916nc_server_endpt_set_address(const char *endpt_name, const char *address)
1917{
1918 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1919}
1920
1921API int
1922nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1923{
1924 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1925}
1926
1927API int
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001928nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
1929{
1930 struct nc_endpt *endpt;
1931 uint16_t i;
1932 int ret = 0;
1933
1934 if (!endpt_name) {
1935 ERRARG("endpt_name");
1936 return -1;
1937 } else if (mode == 0) {
1938 ERRARG("mode");
1939 return -1;
1940 }
1941
1942 /* ENDPT LOCK */
1943 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
1944 if (!endpt)
1945 return -1;
1946
1947 if (endpt->ti != NC_TI_UNIX) {
1948 ret = -1;
1949 goto cleanup;
1950 }
1951
1952 endpt->opts.unixsock->mode = mode;
1953 endpt->opts.unixsock->uid = uid;
1954 endpt->opts.unixsock->gid = gid;
1955
1956cleanup:
1957 /* ENDPT UNLOCK */
1958 pthread_rwlock_unlock(&server_opts.endpt_lock);
1959
1960 return ret;
1961}
1962
1963API int
Michal Vasko59050372016-11-22 14:33:55 +01001964nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001965{
1966 uint32_t i;
1967 int ret = -1;
1968
Michal Vaskoade892d2017-02-22 13:40:35 +01001969 /* BIND LOCK */
1970 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001971
Michal Vaskoade892d2017-02-22 13:40:35 +01001972 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001973 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001974
Michal Vasko59050372016-11-22 14:33:55 +01001975 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001976 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001977 for (i = 0; i < server_opts.endpt_count; ++i) {
1978 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001979 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001980#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001981 case NC_TI_LIBSSH:
1982 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1983 free(server_opts.endpts[i].opts.ssh);
1984 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001985#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001986#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001987 case NC_TI_OPENSSL:
1988 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1989 free(server_opts.endpts[i].opts.tls);
1990 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001991#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001992 case NC_TI_UNIX:
1993 free(server_opts.endpts[i].opts.unixsock);
1994 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001995 default:
1996 ERRINT;
1997 /* won't get here ...*/
1998 break;
1999 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002000 ret = 0;
2001 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002002 free(server_opts.endpts);
2003 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002004
2005 /* remove all binds */
2006 for (i = 0; i < server_opts.endpt_count; ++i) {
2007 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2008 if (server_opts.binds[i].sock > -1) {
2009 close(server_opts.binds[i].sock);
2010 }
2011 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002012 free(server_opts.binds);
2013 server_opts.binds = NULL;
2014
Michal Vasko3031aae2016-01-27 16:07:18 +01002015 server_opts.endpt_count = 0;
2016
Michal Vasko1a38c862016-01-15 15:50:07 +01002017 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002018 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002019 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002020 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002021 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01002022 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002023 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002024#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002025 case NC_TI_LIBSSH:
2026 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2027 free(server_opts.endpts[i].opts.ssh);
2028 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002029#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002030#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002031 case NC_TI_OPENSSL:
2032 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2033 free(server_opts.endpts[i].opts.tls);
2034 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002035#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002036 case NC_TI_UNIX:
2037 free(server_opts.endpts[i].opts.unixsock);
2038 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002039 default:
2040 ERRINT;
2041 break;
2042 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002043
Michal Vaskoe2713da2016-08-22 16:06:40 +02002044 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02002045 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2046 if (server_opts.binds[i].sock > -1) {
2047 close(server_opts.binds[i].sock);
2048 }
2049
2050 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002051 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002052 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002053 free(server_opts.binds);
2054 server_opts.binds = NULL;
2055 free(server_opts.endpts);
2056 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002057 } else if (i < server_opts.endpt_count) {
2058 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2059 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002060 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002061
2062 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002063 if (name) {
2064 break;
2065 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002066 }
2067 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002068 }
2069
Michal Vaskoade892d2017-02-22 13:40:35 +01002070 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002071 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002072
Michal Vaskoade892d2017-02-22 13:40:35 +01002073 /* BIND UNLOCK */
2074 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002075
2076 return ret;
2077}
2078
Michal Vasko71090fc2016-05-24 16:37:28 +02002079API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002080nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002081{
Michal Vasko71090fc2016-05-24 16:37:28 +02002082 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002083 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002084 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002085 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002086 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002087
Michal Vasko45e53ae2016-04-07 11:46:03 +02002088 if (!server_opts.ctx) {
2089 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002090 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002091 } else if (!session) {
2092 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002093 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002094 }
2095
Michal Vaskoade892d2017-02-22 13:40:35 +01002096 /* BIND LOCK */
2097 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002098
2099 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002100 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002101 /* BIND UNLOCK */
2102 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002103 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002104 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002105
Michal Vaskoe2713da2016-08-22 16:06:40 +02002106 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002107 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002108 /* BIND UNLOCK */
2109 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002110 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002111 if (!ret) {
2112 return NC_MSG_WOULDBLOCK;
2113 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002114 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002115 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002116
2117 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2118 /* ENDPT READ LOCK */
2119 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2120
2121 /* BIND UNLOCK */
2122 pthread_mutex_unlock(&server_opts.bind_lock);
2123
Michal Vaskob48aa812016-01-18 14:13:09 +01002124 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002125
Michal Vasko131120a2018-05-29 15:44:02 +02002126 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002127 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002128 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002129 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002130 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002131 msgtype = NC_MSG_ERROR;
2132 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002133 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002134 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002135 (*session)->ctx = server_opts.ctx;
2136 (*session)->flags = NC_SESSION_SHAREDCTX;
2137 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
2138 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002139
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002140 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002141#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002142 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2143 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002144 ret = nc_accept_ssh_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
Radek Krejci53691be2016-02-22 13:58:37 +01002154#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002155 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2156 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002157 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002158 if (ret < 0) {
2159 msgtype = NC_MSG_ERROR;
2160 goto cleanup;
2161 } else if (!ret) {
2162 msgtype = NC_MSG_WOULDBLOCK;
2163 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002164 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002165 } else
2166#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002167 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2168 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2169 ret = nc_accept_unix(*session, sock);
2170 if (ret < 0) {
2171 msgtype = NC_MSG_ERROR;
2172 goto cleanup;
2173 }
2174 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002175 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002176 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002177 msgtype = NC_MSG_ERROR;
2178 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002179 }
2180
Michal Vasko2cc4c682016-03-01 09:16:48 +01002181 (*session)->data = NULL;
2182
Michal Vaskoade892d2017-02-22 13:40:35 +01002183 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002184 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002185
Michal Vaskob48aa812016-01-18 14:13:09 +01002186 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002187 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002188
Michal Vasko9e036d52016-01-08 10:49:26 +01002189 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002190 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002191 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002192 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002193 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002194 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002195 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002196
2197 nc_gettimespec_mono(&ts_cur);
2198 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2199 nc_gettimespec_real(&ts_cur);
2200 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002201 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002202
Michal Vasko71090fc2016-05-24 16:37:28 +02002203 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002204
Michal Vasko71090fc2016-05-24 16:37:28 +02002205cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002206 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002207 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002208
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002209 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002210 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002211 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002212}
2213
Michal Vasko2e6defd2016-10-07 15:48:15 +02002214API int
2215nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
2216{
2217 uint16_t i;
2218
2219 if (!name) {
2220 ERRARG("name");
2221 return -1;
2222 } else if (!ti) {
2223 ERRARG("ti");
2224 return -1;
2225 }
2226
2227 /* WRITE LOCK */
2228 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2229
2230 /* check name uniqueness */
2231 for (i = 0; i < server_opts.ch_client_count; ++i) {
2232 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2233 ERR("Call Home client \"%s\" already exists.", name);
2234 /* WRITE UNLOCK */
2235 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2236 return -1;
2237 }
2238 }
2239
2240 ++server_opts.ch_client_count;
2241 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2242 if (!server_opts.ch_clients) {
2243 ERRMEM;
2244 /* WRITE UNLOCK */
2245 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2246 return -1;
2247 }
2248 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 +01002249 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 +02002250 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002251 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
2252 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002253
2254 switch (ti) {
2255#ifdef NC_ENABLED_SSH
2256 case NC_TI_LIBSSH:
2257 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2258 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2259 ERRMEM;
2260 /* WRITE UNLOCK */
2261 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2262 return -1;
2263 }
2264 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2265 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2266 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2267 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2268 break;
2269#endif
2270#ifdef NC_ENABLED_TLS
2271 case NC_TI_OPENSSL:
2272 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2273 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2274 ERRMEM;
2275 /* WRITE UNLOCK */
2276 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2277 return -1;
2278 }
2279 break;
2280#endif
2281 default:
2282 ERRINT;
2283 /* WRITE UNLOCK */
2284 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2285 return -1;
2286 }
2287
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002288 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2289
Michal Vasko2e6defd2016-10-07 15:48:15 +02002290 /* set CH default options */
2291 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2292 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2293
2294 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2295
2296 /* WRITE UNLOCK */
2297 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2298
2299 return 0;
2300}
2301
2302API int
Michal Vasko59050372016-11-22 14:33:55 +01002303nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002304{
2305 uint16_t i, j;
2306 int ret = -1;
2307
2308 /* WRITE LOCK */
2309 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2310
Michal Vasko59050372016-11-22 14:33:55 +01002311 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002312 /* remove all CH clients */
2313 for (i = 0; i < server_opts.ch_client_count; ++i) {
2314 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2315
2316 /* remove all endpoints */
2317 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2318 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2319 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2320 }
2321 free(server_opts.ch_clients[i].ch_endpts);
2322
2323 switch (server_opts.ch_clients[i].ti) {
2324#ifdef NC_ENABLED_SSH
2325 case NC_TI_LIBSSH:
2326 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2327 free(server_opts.ch_clients[i].opts.ssh);
2328 break;
2329#endif
2330#ifdef NC_ENABLED_TLS
2331 case NC_TI_OPENSSL:
2332 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2333 free(server_opts.ch_clients[i].opts.tls);
2334 break;
2335#endif
2336 default:
2337 ERRINT;
2338 /* won't get here ...*/
2339 break;
2340 }
2341
2342 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2343
2344 ret = 0;
2345 }
2346 free(server_opts.ch_clients);
2347 server_opts.ch_clients = NULL;
2348
2349 server_opts.ch_client_count = 0;
2350
2351 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002352 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002353 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002354 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 +02002355 /* remove endpt */
2356 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2357
2358 switch (server_opts.ch_clients[i].ti) {
2359#ifdef NC_ENABLED_SSH
2360 case NC_TI_LIBSSH:
2361 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2362 free(server_opts.ch_clients[i].opts.ssh);
2363 break;
2364#endif
2365#ifdef NC_ENABLED_TLS
2366 case NC_TI_OPENSSL:
2367 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2368 free(server_opts.ch_clients[i].opts.tls);
2369 break;
2370#endif
2371 default:
2372 ERRINT;
2373 break;
2374 }
2375
2376 /* remove all endpoints */
2377 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2378 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2379 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2380 }
2381 free(server_opts.ch_clients[i].ch_endpts);
2382
2383 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2384
2385 /* move last client and endpoint(s) to the empty space */
2386 --server_opts.ch_client_count;
2387 if (i < server_opts.ch_client_count) {
2388 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2389 sizeof *server_opts.ch_clients);
2390 } else if (!server_opts.ch_client_count) {
2391 free(server_opts.ch_clients);
2392 server_opts.ch_clients = NULL;
2393 }
2394
2395 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002396 if (name) {
2397 break;
2398 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002399 }
2400 }
2401 }
2402
2403 /* WRITE UNLOCK */
2404 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2405
2406 return ret;
2407}
2408
2409API int
2410nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2411{
2412 uint16_t i;
2413 struct nc_ch_client *client;
2414
2415 if (!client_name) {
2416 ERRARG("client_name");
2417 return -1;
2418 } else if (!endpt_name) {
2419 ERRARG("endpt_name");
2420 return -1;
2421 }
2422
2423 /* LOCK */
2424 client = nc_server_ch_client_lock(client_name, 0, NULL);
2425 if (!client) {
2426 return -1;
2427 }
2428
2429 for (i = 0; i < client->ch_endpt_count; ++i) {
2430 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2431 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2432 /* UNLOCK */
2433 nc_server_ch_client_unlock(client);
2434 return -1;
2435 }
2436 }
2437
2438 ++client->ch_endpt_count;
2439 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2440 if (!client->ch_endpts) {
2441 ERRMEM;
2442 /* UNLOCK */
2443 nc_server_ch_client_unlock(client);
2444 return -1;
2445 }
2446
2447 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2448 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2449 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
Frank Rimpler9f838b02018-07-25 06:44:03 +00002450 client->ch_endpts[client->ch_endpt_count - 1].sock_pending = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002451
2452 /* UNLOCK */
2453 nc_server_ch_client_unlock(client);
2454
2455 return 0;
2456}
2457
2458API int
2459nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2460{
2461 uint16_t i;
2462 int ret = -1;
2463 struct nc_ch_client *client;
2464
2465 if (!client_name) {
2466 ERRARG("client_name");
2467 return -1;
2468 }
2469
2470 /* LOCK */
2471 client = nc_server_ch_client_lock(client_name, 0, NULL);
2472 if (!client) {
2473 return -1;
2474 }
2475
2476 if (!endpt_name) {
2477 /* remove all endpoints */
2478 for (i = 0; i < client->ch_endpt_count; ++i) {
2479 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2480 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Frank Rimpler9f838b02018-07-25 06:44:03 +00002481 if (client->ch_endpts[i].sock_pending != -1) {
2482 close(client->ch_endpts[i].sock_pending);
2483 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002484 }
2485 free(client->ch_endpts);
2486 client->ch_endpts = NULL;
2487 client->ch_endpt_count = 0;
2488
2489 ret = 0;
2490 } else {
2491 for (i = 0; i < client->ch_endpt_count; ++i) {
2492 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2493 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2494 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002495
Michal Vasko4f921012016-10-20 14:07:45 +02002496 /* move last endpoint to the empty space */
2497 --client->ch_endpt_count;
2498 if (i < client->ch_endpt_count) {
2499 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2500 } else if (!server_opts.ch_client_count) {
2501 free(server_opts.ch_clients);
2502 server_opts.ch_clients = NULL;
2503 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002504
Michal Vasko4f921012016-10-20 14:07:45 +02002505 ret = 0;
2506 break;
2507 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002508 }
2509 }
2510
2511 /* UNLOCK */
2512 nc_server_ch_client_unlock(client);
2513
2514 return ret;
2515}
2516
2517API int
2518nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2519{
2520 uint16_t i;
2521 int ret = -1;
2522 struct nc_ch_client *client;
2523
2524 if (!client_name) {
2525 ERRARG("client_name");
2526 return -1;
2527 } else if (!endpt_name) {
2528 ERRARG("endpt_name");
2529 return -1;
2530 } else if (!address) {
2531 ERRARG("address");
2532 return -1;
2533 }
2534
2535 /* LOCK */
2536 client = nc_server_ch_client_lock(client_name, 0, NULL);
2537 if (!client) {
2538 return -1;
2539 }
2540
2541 for (i = 0; i < client->ch_endpt_count; ++i) {
2542 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2543 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2544 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2545
2546 ret = 0;
2547 break;
2548 }
2549 }
2550
2551 /* UNLOCK */
2552 nc_server_ch_client_unlock(client);
2553
2554 if (ret == -1) {
2555 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2556 }
2557
2558 return ret;
2559}
2560
2561API int
2562nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2563{
2564 uint16_t i;
2565 int ret = -1;
2566 struct nc_ch_client *client;
2567
2568 if (!client_name) {
2569 ERRARG("client_name");
2570 return -1;
2571 } else if (!endpt_name) {
2572 ERRARG("endpt_name");
2573 return -1;
2574 } else if (!port) {
2575 ERRARG("port");
2576 return -1;
2577 }
2578
2579 /* LOCK */
2580 client = nc_server_ch_client_lock(client_name, 0, NULL);
2581 if (!client) {
2582 return -1;
2583 }
2584
2585 for (i = 0; i < client->ch_endpt_count; ++i) {
2586 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2587 client->ch_endpts[i].port = port;
2588
2589 ret = 0;
2590 break;
2591 }
2592 }
2593
2594 /* UNLOCK */
2595 nc_server_ch_client_unlock(client);
2596
2597 if (ret == -1) {
2598 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2599 }
2600
2601 return ret;
2602}
2603
2604API int
2605nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2606{
2607 struct nc_ch_client *client;
2608
2609 if (!client_name) {
2610 ERRARG("client_name");
2611 return -1;
2612 } else if (!conn_type) {
2613 ERRARG("conn_type");
2614 return -1;
2615 }
2616
2617 /* LOCK */
2618 client = nc_server_ch_client_lock(client_name, 0, NULL);
2619 if (!client) {
2620 return -1;
2621 }
2622
2623 if (client->conn_type != conn_type) {
2624 client->conn_type = conn_type;
2625
2626 /* set default options */
2627 switch (conn_type) {
2628 case NC_CH_PERSIST:
2629 client->conn.persist.idle_timeout = 86400;
2630 client->conn.persist.ka_max_wait = 30;
2631 client->conn.persist.ka_max_attempts = 3;
2632 break;
2633 case NC_CH_PERIOD:
2634 client->conn.period.idle_timeout = 300;
2635 client->conn.period.reconnect_timeout = 60;
2636 break;
2637 default:
2638 ERRINT;
2639 break;
2640 }
2641 }
2642
2643 /* UNLOCK */
2644 nc_server_ch_client_unlock(client);
2645
2646 return 0;
2647}
2648
2649API int
2650nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2651{
2652 struct nc_ch_client *client;
2653
2654 if (!client_name) {
2655 ERRARG("client_name");
2656 return -1;
2657 }
2658
2659 /* LOCK */
2660 client = nc_server_ch_client_lock(client_name, 0, NULL);
2661 if (!client) {
2662 return -1;
2663 }
2664
2665 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002666 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002667 /* UNLOCK */
2668 nc_server_ch_client_unlock(client);
2669 return -1;
2670 }
2671
2672 client->conn.persist.idle_timeout = idle_timeout;
2673
2674 /* UNLOCK */
2675 nc_server_ch_client_unlock(client);
2676
2677 return 0;
2678}
2679
2680API int
2681nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2682{
2683 struct nc_ch_client *client;
2684
2685 if (!client_name) {
2686 ERRARG("client_name");
2687 return -1;
2688 } else if (!max_wait) {
2689 ERRARG("max_wait");
2690 return -1;
2691 }
2692
2693 /* LOCK */
2694 client = nc_server_ch_client_lock(client_name, 0, NULL);
2695 if (!client) {
2696 return -1;
2697 }
2698
2699 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002700 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002701 /* UNLOCK */
2702 nc_server_ch_client_unlock(client);
2703 return -1;
2704 }
2705
2706 client->conn.persist.ka_max_wait = max_wait;
2707
2708 /* UNLOCK */
2709 nc_server_ch_client_unlock(client);
2710
2711 return 0;
2712}
2713
2714API int
2715nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2716{
2717 struct nc_ch_client *client;
2718
2719 if (!client_name) {
2720 ERRARG("client_name");
2721 return -1;
2722 }
2723
2724 /* LOCK */
2725 client = nc_server_ch_client_lock(client_name, 0, NULL);
2726 if (!client) {
2727 return -1;
2728 }
2729
2730 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002731 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002732 /* UNLOCK */
2733 nc_server_ch_client_unlock(client);
2734 return -1;
2735 }
2736
2737 client->conn.persist.ka_max_attempts = max_attempts;
2738
2739 /* UNLOCK */
2740 nc_server_ch_client_unlock(client);
2741
2742 return 0;
2743}
2744
2745API int
2746nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2747{
2748 struct nc_ch_client *client;
2749
2750 if (!client_name) {
2751 ERRARG("client_name");
2752 return -1;
2753 }
2754
2755 /* LOCK */
2756 client = nc_server_ch_client_lock(client_name, 0, NULL);
2757 if (!client) {
2758 return -1;
2759 }
2760
2761 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002762 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002763 /* UNLOCK */
2764 nc_server_ch_client_unlock(client);
2765 return -1;
2766 }
2767
2768 client->conn.period.idle_timeout = idle_timeout;
2769
2770 /* UNLOCK */
2771 nc_server_ch_client_unlock(client);
2772
2773 return 0;
2774}
2775
2776API int
2777nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2778{
2779 struct nc_ch_client *client;
2780
2781 if (!client_name) {
2782 ERRARG("client_name");
2783 return -1;
2784 } else if (!reconnect_timeout) {
2785 ERRARG("reconnect_timeout");
2786 return -1;
2787 }
2788
2789 /* LOCK */
2790 client = nc_server_ch_client_lock(client_name, 0, NULL);
2791 if (!client) {
2792 return -1;
2793 }
2794
2795 if (client->conn_type != NC_CH_PERIOD) {
2796 ERR("Call Home client \"%s\" is not of periodic connection type.");
2797 /* UNLOCK */
2798 nc_server_ch_client_unlock(client);
2799 return -1;
2800 }
2801
2802 client->conn.period.reconnect_timeout = reconnect_timeout;
2803
2804 /* UNLOCK */
2805 nc_server_ch_client_unlock(client);
2806
2807 return 0;
2808}
2809
2810API int
2811nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2812{
2813 struct nc_ch_client *client;
2814
2815 if (!client_name) {
2816 ERRARG("client_name");
2817 return -1;
2818 }
2819
2820 /* LOCK */
2821 client = nc_server_ch_client_lock(client_name, 0, NULL);
2822 if (!client) {
2823 return -1;
2824 }
2825
2826 client->start_with = start_with;
2827
2828 /* UNLOCK */
2829 nc_server_ch_client_unlock(client);
2830
2831 return 0;
2832}
2833
2834API int
2835nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2836{
2837 struct nc_ch_client *client;
2838
2839 if (!client_name) {
2840 ERRARG("client_name");
2841 return -1;
2842 } else if (!max_attempts) {
2843 ERRARG("max_attempts");
2844 return -1;
2845 }
2846
2847 /* LOCK */
2848 client = nc_server_ch_client_lock(client_name, 0, NULL);
2849 if (!client) {
2850 return -1;
2851 }
2852
2853 client->max_attempts = max_attempts;
2854
2855 /* UNLOCK */
2856 nc_server_ch_client_unlock(client);
2857
2858 return 0;
2859}
2860
2861/* client lock is expected to be held */
2862static NC_MSG_TYPE
2863nc_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 +01002864{
Michal Vasko71090fc2016-05-24 16:37:28 +02002865 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002866 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002867 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01002868 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01002869
Michal Vasko66032bc2019-01-22 15:03:12 +01002870 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002871 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002872 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002873 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00002874 /* no need to store the socket as pending any longer */
2875 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01002876
Michal Vasko131120a2018-05-29 15:44:02 +02002877 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002878 if (!(*session)) {
2879 ERRMEM;
2880 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01002881 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002882 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002883 }
2884 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01002885 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002886 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko66032bc2019-01-22 15:03:12 +01002887 (*session)->host = lydict_insert_zc(server_opts.ctx, ip_host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002888 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002889
Michal Vaskob05053d2016-01-22 16:12:06 +01002890 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002891#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002892 if (client->ti == NC_TI_LIBSSH) {
2893 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002894 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002895 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002896
Michal Vasko71090fc2016-05-24 16:37:28 +02002897 if (ret < 0) {
2898 msgtype = NC_MSG_ERROR;
2899 goto fail;
2900 } else if (!ret) {
2901 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002902 goto fail;
2903 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002904 } else
2905#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002906#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002907 if (client->ti == NC_TI_OPENSSL) {
2908 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002909 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002910 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002911
Michal Vasko71090fc2016-05-24 16:37:28 +02002912 if (ret < 0) {
2913 msgtype = NC_MSG_ERROR;
2914 goto fail;
2915 } else if (!ret) {
2916 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002917 goto fail;
2918 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002919 } else
2920#endif
2921 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002922 ERRINT;
2923 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002924 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002925 goto fail;
2926 }
2927
2928 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002929 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01002930
2931 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002932 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002933 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002934 goto fail;
2935 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002936
2937 nc_gettimespec_mono(&ts_cur);
2938 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2939 nc_gettimespec_real(&ts_cur);
2940 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01002941 (*session)->status = NC_STATUS_RUNNING;
2942
Michal Vasko71090fc2016-05-24 16:37:28 +02002943 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002944
2945fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002946 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002947 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002948 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002949}
2950
Michal Vasko2e6defd2016-10-07 15:48:15 +02002951struct nc_ch_client_thread_arg {
2952 char *client_name;
2953 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2954};
2955
2956static struct nc_ch_client *
2957nc_server_ch_client_with_endpt_lock(const char *name)
2958{
2959 struct nc_ch_client *client;
2960
2961 while (1) {
2962 /* LOCK */
2963 client = nc_server_ch_client_lock(name, 0, NULL);
2964 if (!client) {
2965 return NULL;
2966 }
2967 if (client->ch_endpt_count) {
2968 return client;
2969 }
2970 /* no endpoints defined yet */
2971
2972 /* UNLOCK */
2973 nc_server_ch_client_unlock(client);
2974
2975 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2976 }
2977
2978 return NULL;
2979}
2980
2981static int
2982nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2983{
Michal Vasko3f05a092018-03-13 10:39:49 +01002984 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002985 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002986 struct timespec ts;
2987 struct nc_ch_client *client;
2988
2989 /* session created, initialize condition */
Michal Vasko27377422018-03-15 08:59:35 +01002990 session->opts.server.ch_lock = calloc(1, sizeof *session->opts.server.ch_lock);
2991 session->opts.server.ch_cond = calloc(1, sizeof *session->opts.server.ch_cond);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002992 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2993 ERRMEM;
2994 nc_session_free(session, NULL);
2995 return -1;
2996 }
2997 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2998 pthread_cond_init(session->opts.server.ch_cond, NULL);
2999
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003000 session->flags |= NC_SESSION_CALLHOME;
3001
Michal Vasko2e6defd2016-10-07 15:48:15 +02003002 /* CH LOCK */
3003 pthread_mutex_lock(session->opts.server.ch_lock);
3004
3005 /* give the session to the user */
3006 data->session_clb(data->client_name, session);
3007
3008 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003009 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003010 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003011
Michal Vasko3f05a092018-03-13 10:39:49 +01003012 r = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
3013 if (!r) {
3014 /* we were woken up, something probably happened */
3015 if (session->status != NC_STATUS_RUNNING) {
3016 break;
3017 }
3018 } else if (r != ETIMEDOUT) {
3019 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3020 ret = -1;
3021 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003022 }
3023
Michal Vasko2e6defd2016-10-07 15:48:15 +02003024 /* check whether the client was not removed */
3025 /* LOCK */
3026 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
3027 if (!client) {
3028 /* client was removed, finish thread */
3029 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
3030 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003031 ret = 1;
3032 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003033 }
3034
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003035 if (client->conn_type == NC_CH_PERSIST) {
3036 /* TODO keep-alives */
3037 idle_timeout = client->conn.persist.idle_timeout;
3038 } else {
3039 idle_timeout = client->conn.period.idle_timeout;
3040 }
3041
Michal Vasko9fb42272017-10-05 13:50:05 +02003042 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003043 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 +02003044 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3045 session->status = NC_STATUS_INVALID;
3046 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3047 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003048
3049 /* UNLOCK */
3050 nc_server_ch_client_unlock(client);
3051
3052 } while (session->status == NC_STATUS_RUNNING);
3053
Michal Vasko27377422018-03-15 08:59:35 +01003054 /* CH UNLOCK */
3055 pthread_mutex_unlock(session->opts.server.ch_lock);
3056
Michal Vasko3f05a092018-03-13 10:39:49 +01003057 if (session->status == NC_STATUS_CLOSING) {
3058 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
3059 session->flags &= ~NC_SESSION_CALLHOME;
3060 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003061
Michal Vasko3f05a092018-03-13 10:39:49 +01003062 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003063}
3064
3065static void *
3066nc_ch_client_thread(void *arg)
3067{
3068 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3069 NC_MSG_TYPE msgtype;
3070 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003071 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003072 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003073 struct nc_ch_endpt *cur_endpt;
3074 struct nc_session *session;
3075 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003076 uint32_t client_id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003077
3078 /* LOCK */
3079 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3080 if (!client) {
3081 goto cleanup;
3082 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003083 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003084
3085 cur_endpt = &client->ch_endpts[0];
3086 cur_endpt_name = strdup(cur_endpt->name);
3087
Michal Vasko29af44b2016-10-13 10:59:55 +02003088 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003089 while (1) {
3090 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
3091
3092 if (msgtype == NC_MSG_HELLO) {
3093 /* UNLOCK */
3094 nc_server_ch_client_unlock(client);
3095
Michal Vasko29af44b2016-10-13 10:59:55 +02003096 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003097 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3098 goto cleanup;
3099 }
Michal Vasko29af44b2016-10-13 10:59:55 +02003100 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003101
3102 /* LOCK */
3103 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3104 if (!client) {
3105 goto cleanup;
3106 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003107 if (client->id != client_id) {
3108 nc_server_ch_client_unlock(client);
3109 goto cleanup;
3110 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003111
3112 /* session changed status -> it was disconnected for whatever reason,
3113 * persistent connection immediately tries to reconnect, periodic waits some first */
3114 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003115 /* UNLOCK */
3116 nc_server_ch_client_unlock(client);
3117
3118 /* TODO wake up sometimes to check for new notifications */
3119 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
3120
3121 /* LOCK */
3122 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3123 if (!client) {
3124 goto cleanup;
3125 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003126 if (client->id != client_id) {
3127 nc_server_ch_client_unlock(client);
3128 goto cleanup;
3129 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003130 }
3131
3132 /* set next endpoint to try */
3133 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003134 next_endpt_index = 0;
Michal Vasko2a225342018-09-05 08:38:34 +02003135 } else {
Peter Feiged05f2252018-09-03 08:09:47 +00003136 /* we keep the current one but due to unlock/lock we have to find it again */
3137 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3138 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3139 break;
3140 }
3141 }
3142 if (next_endpt_index >= client->ch_endpt_count) {
3143 /* endpoint was removed, start with the first one */
3144 next_endpt_index = 0;
3145 }
3146 }
3147
Michal Vasko2e6defd2016-10-07 15:48:15 +02003148 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003149 /* UNLOCK */
3150 nc_server_ch_client_unlock(client);
3151
Michal Vasko2e6defd2016-10-07 15:48:15 +02003152 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003153 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3154
Michal Vasko6bb116b2016-10-26 13:53:46 +02003155 /* LOCK */
3156 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3157 if (!client) {
3158 goto cleanup;
3159 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003160 if (client->id != client_id) {
3161 nc_server_ch_client_unlock(client);
3162 goto cleanup;
3163 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003164
Michal Vasko2e6defd2016-10-07 15:48:15 +02003165 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003166
3167 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003168 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3169 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003170 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003171 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003172 }
3173
Peter Feiged05f2252018-09-03 08:09:47 +00003174 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003175 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003176 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003177 cur_attempts = 0;
3178 } else if (cur_attempts == client->max_attempts) {
3179 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003180 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003181 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003182 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003183 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003184 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003185 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003186 }
3187
3188 cur_attempts = 0;
3189 } /* else we keep the current one */
3190 }
Peter Feiged05f2252018-09-03 08:09:47 +00003191
3192 cur_endpt = &client->ch_endpts[next_endpt_index];
3193 free(cur_endpt_name);
3194 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003195 }
3196
3197cleanup:
3198 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003199 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003200 free(data->client_name);
3201 free(data);
3202 return NULL;
3203}
3204
3205API int
3206nc_connect_ch_client_dispatch(const char *client_name,
Michal Vasko3f05a092018-03-13 10:39:49 +01003207 void (*session_clb)(const char *client_name, struct nc_session *new_session))
3208{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003209 int ret;
3210 pthread_t tid;
3211 struct nc_ch_client_thread_arg *arg;
3212
3213 if (!client_name) {
3214 ERRARG("client_name");
3215 return -1;
3216 } else if (!session_clb) {
3217 ERRARG("session_clb");
3218 return -1;
3219 }
3220
3221 arg = malloc(sizeof *arg);
3222 if (!arg) {
3223 ERRMEM;
3224 return -1;
3225 }
3226 arg->client_name = strdup(client_name);
3227 if (!arg->client_name) {
3228 ERRMEM;
3229 free(arg);
3230 return -1;
3231 }
3232 arg->session_clb = session_clb;
3233
3234 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3235 if (ret) {
3236 ERR("Creating a new thread failed (%s).", strerror(ret));
3237 free(arg->client_name);
3238 free(arg);
3239 return -1;
3240 }
3241 /* the thread now manages arg */
3242
3243 pthread_detach(tid);
3244
3245 return 0;
3246}
3247
Radek Krejci53691be2016-02-22 13:58:37 +01003248#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003249
Michal Vaskoe8e07702017-03-15 10:19:30 +01003250API int
3251nc_server_endpt_count(void)
3252{
3253 return server_opts.endpt_count;
3254}
3255
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003256API time_t
3257nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003258{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003259 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003260 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003261 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003262 }
3263
Michal Vasko2e6defd2016-10-07 15:48:15 +02003264 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003265}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003266
3267API void
3268nc_session_set_notif_status(struct nc_session *session, int notif_status)
3269{
3270 if (!session || (session->side != NC_SERVER)) {
3271 ERRARG("session");
3272 return;
3273 }
3274
3275 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3276}
3277
3278API int
3279nc_session_get_notif_status(const struct nc_session *session)
3280{
3281 if (!session || (session->side != NC_SERVER)) {
3282 ERRARG("session");
3283 return 0;
3284 }
3285
3286 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003287}
Michal Vasko8f430592019-02-26 08:32:54 +01003288
3289API int
3290nc_session_is_callhome(const struct nc_session *session)
3291{
3292 if (!session || (session->side != NC_SERVER)) {
3293 ERRARG("session");
3294 return 0;
3295 }
3296
3297 if (session->flags & NC_SESSION_CALLHOME) {
3298 return 1;
3299 }
3300
3301 return 0;
3302}